1 | /* |
2 | Copyright (c) 2005-2019 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 | #if _MSC_VER && !defined(__INTEL_COMPILER) |
18 | #pragma warning(disable: 4180) // "qualifier applied to function type has no meaning; ignored" |
19 | #endif |
20 | |
21 | #include "tbb/parallel_for_each.h" |
22 | #include "tbb/task_scheduler_init.h" |
23 | #include "tbb/atomic.h" |
24 | #include "harness.h" |
25 | #include "harness_iterator.h" |
26 | #include <list> |
27 | |
28 | // Some old compilers can't deduce template parameter type for parallel_for_each |
29 | // if the function name is passed without explicit cast to function pointer. |
30 | typedef void (*TestFunctionType)(size_t); |
31 | |
32 | tbb::atomic<size_t> sum; |
33 | |
34 | // This function is called via parallel_for_each |
35 | void TestFunction (size_t value) { |
36 | sum += (unsigned int)value; |
37 | } |
38 | |
39 | const size_t NUMBER_OF_ELEMENTS = 1000; |
40 | |
41 | // Tests tbb::parallel_for_each functionality |
42 | template <typename Iterator> |
43 | void RunPForEachTests() |
44 | { |
45 | size_t test_vector[NUMBER_OF_ELEMENTS + 1]; |
46 | |
47 | sum = 0; |
48 | size_t test_sum = 0; |
49 | |
50 | for (size_t i =0; i < NUMBER_OF_ELEMENTS; i++) { |
51 | test_vector[i] = i; |
52 | test_sum += i; |
53 | } |
54 | test_vector[NUMBER_OF_ELEMENTS] = 1000000; // parallel_for_each shouldn't touch this element |
55 | |
56 | Iterator begin(&test_vector[0]); |
57 | Iterator end(&test_vector[NUMBER_OF_ELEMENTS]); |
58 | |
59 | tbb::parallel_for_each(begin, end, (TestFunctionType)TestFunction); |
60 | ASSERT(sum == test_sum, "Not all items of test vector were processed by parallel_for_each" ); |
61 | ASSERT(test_vector[NUMBER_OF_ELEMENTS] == 1000000, "parallel_for_each processed an extra element" ); |
62 | } |
63 | |
64 | typedef void (*TestMutatorType)(size_t&); |
65 | |
66 | void TestMutator(size_t& value) { |
67 | ASSERT(value==0,NULL); |
68 | ++sum; |
69 | ++value; |
70 | } |
71 | |
72 | //! Test that tbb::parallel_for_each works for mutable iterators. |
73 | template <typename Iterator> |
74 | void RunMutablePForEachTests() { |
75 | size_t test_vector[NUMBER_OF_ELEMENTS]; |
76 | for( size_t i=0; i<NUMBER_OF_ELEMENTS; ++i ) |
77 | test_vector[i] = 0; |
78 | sum = 0; |
79 | tbb::parallel_for_each( Iterator(test_vector), Iterator(test_vector+NUMBER_OF_ELEMENTS), (TestMutatorType)TestMutator ); |
80 | ASSERT( sum==NUMBER_OF_ELEMENTS, "parallel_for_each called function wrong number of times" ); |
81 | for( size_t i=0; i<NUMBER_OF_ELEMENTS; ++i ) |
82 | ASSERT( test_vector[i]==1, "parallel_for_each did not process each element exactly once" ); |
83 | } |
84 | |
85 | #if __TBB_TASK_GROUP_CONTEXT |
86 | #define HARNESS_EH_SIMPLE_MODE 1 |
87 | #include "tbb/tbb_exception.h" |
88 | #include "harness_eh.h" |
89 | |
90 | #if TBB_USE_EXCEPTIONS |
91 | void test_function_with_exception(size_t) { |
92 | ThrowTestException(); |
93 | } |
94 | |
95 | template <typename Iterator> |
96 | void TestExceptionsSupport() |
97 | { |
98 | REMARK (__FUNCTION__); |
99 | size_t test_vector[NUMBER_OF_ELEMENTS + 1]; |
100 | |
101 | for (size_t i = 0; i < NUMBER_OF_ELEMENTS; i++) { |
102 | test_vector[i] = i; |
103 | } |
104 | |
105 | Iterator begin(&test_vector[0]); |
106 | Iterator end(&test_vector[NUMBER_OF_ELEMENTS]); |
107 | |
108 | TRY(); |
109 | tbb::parallel_for_each(begin, end, (TestFunctionType)test_function_with_exception); |
110 | CATCH_AND_ASSERT(); |
111 | } |
112 | #endif /* TBB_USE_EXCEPTIONS */ |
113 | |
114 | // Cancellation support test |
115 | void function_to_cancel(size_t ) { |
116 | ++g_CurExecuted; |
117 | CancellatorTask::WaitUntilReady(); |
118 | } |
119 | |
120 | template <typename Iterator> |
121 | class my_worker_pforeach_task : public tbb::task |
122 | { |
123 | tbb::task_group_context &my_ctx; |
124 | |
125 | tbb::task* execute () __TBB_override { |
126 | size_t test_vector[NUMBER_OF_ELEMENTS + 1]; |
127 | for (size_t i = 0; i < NUMBER_OF_ELEMENTS; i++) { |
128 | test_vector[i] = i; |
129 | } |
130 | Iterator begin(&test_vector[0]); |
131 | Iterator end(&test_vector[NUMBER_OF_ELEMENTS]); |
132 | |
133 | tbb::parallel_for_each(begin, end, (TestFunctionType)function_to_cancel); |
134 | |
135 | return NULL; |
136 | } |
137 | public: |
138 | my_worker_pforeach_task ( tbb::task_group_context &ctx) : my_ctx(ctx) { } |
139 | }; |
140 | |
141 | template <typename Iterator> |
142 | void TestCancellation() |
143 | { |
144 | REMARK (__FUNCTION__); |
145 | ResetEhGlobals(); |
146 | RunCancellationTest<my_worker_pforeach_task<Iterator>, CancellatorTask>(); |
147 | } |
148 | #endif /* __TBB_TASK_GROUP_CONTEXT */ |
149 | |
150 | #include "harness_cpu.h" |
151 | |
152 | const size_t elements = 10000; |
153 | const size_t init_sum = 0; |
154 | tbb::atomic<size_t> element_counter; |
155 | |
156 | template<size_t K> |
157 | struct set_to { |
158 | void operator()(size_t& x) const { |
159 | x = K; |
160 | ++element_counter; |
161 | } |
162 | }; |
163 | |
164 | #include "test_range_based_for.h" |
165 | #include <functional> |
166 | |
167 | void range_for_each_test() { |
168 | using namespace range_based_for_support_tests; |
169 | std::list<size_t> v(elements, 0); |
170 | |
171 | // iterator, const and non-const range check |
172 | element_counter = 0; |
173 | tbb::parallel_for_each(v.begin(), v.end(), set_to<1>()); |
174 | ASSERT(element_counter == v.size() && element_counter == elements, "not all elements were set" ); |
175 | ASSERT(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == v.size(), "elements of v not all ones" ); |
176 | |
177 | element_counter = 0; |
178 | tbb::parallel_for_each(v, set_to<0>()); |
179 | ASSERT(element_counter == v.size() && element_counter == elements, "not all elements were set" ); |
180 | ASSERT(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == init_sum , "elements of v not all zeros" ); |
181 | |
182 | element_counter = 0; |
183 | tbb::parallel_for_each(tbb::blocked_range<std::list<size_t>::iterator>(v.begin(), v.end()), set_to<1>()); |
184 | ASSERT(element_counter == v.size() && element_counter == elements, "not all elements were set" ); |
185 | ASSERT(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == v.size(), "elements of v not all ones" ); |
186 | |
187 | // iterator, const and non-const range check with context |
188 | element_counter = 0; |
189 | tbb::task_group_context context; |
190 | tbb::parallel_for_each(v.begin(), v.end(), set_to<0>(), context); |
191 | ASSERT(element_counter == v.size() && element_counter == elements, "not all elements were set" ); |
192 | ASSERT(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == init_sum , "elements of v not all zeros" ); |
193 | |
194 | element_counter = 0; |
195 | tbb::parallel_for_each(v, set_to<1>(), context); |
196 | ASSERT(element_counter == v.size() && element_counter == elements, "not all elements were set" ); |
197 | ASSERT(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == v.size(), "elements of v not all ones" ); |
198 | |
199 | element_counter = 0; |
200 | tbb::parallel_for_each(tbb::blocked_range<std::list<size_t>::iterator>(v.begin(), v.end()), set_to<0>(), context); |
201 | ASSERT(element_counter == v.size() && element_counter == elements, "not all elements were set" ); |
202 | ASSERT(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == init_sum , "elements of v not all zeros" ); |
203 | } |
204 | |
205 | int TestMain () { |
206 | if( MinThread<1 ) { |
207 | REPORT("number of threads must be positive\n" ); |
208 | exit(1); |
209 | } |
210 | for( int p=MinThread; p<=MaxThread; ++p ) { |
211 | tbb::task_scheduler_init init( p ); |
212 | |
213 | RunPForEachTests<Harness::RandomIterator<size_t> >(); |
214 | RunPForEachTests<Harness::ConstRandomIterator<size_t> >(); |
215 | RunPForEachTests<Harness::InputIterator<size_t> >(); |
216 | RunPForEachTests<Harness::ForwardIterator<size_t> >(); |
217 | |
218 | RunMutablePForEachTests<Harness::RandomIterator<size_t> >(); |
219 | RunMutablePForEachTests<Harness::ForwardIterator<size_t> >(); |
220 | |
221 | #if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN |
222 | TestExceptionsSupport<Harness::RandomIterator<size_t> >(); |
223 | TestExceptionsSupport<Harness::InputIterator<size_t> >(); |
224 | TestExceptionsSupport<Harness::ForwardIterator<size_t> >(); |
225 | #endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */ |
226 | |
227 | #if __TBB_TASK_GROUP_CONTEXT |
228 | if (p > 1) { |
229 | TestCancellation<Harness::RandomIterator<size_t> >(); |
230 | TestCancellation<Harness::InputIterator<size_t> >(); |
231 | TestCancellation<Harness::ForwardIterator<size_t> >(); |
232 | } |
233 | #endif /* __TBB_TASK_GROUP_CONTEXT */ |
234 | |
235 | range_for_each_test(); |
236 | |
237 | // Test that all workers sleep when no work |
238 | TestCPUUserTime(p); |
239 | } |
240 | #if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN |
241 | REPORT("Known issue: exception handling tests are skipped.\n" ); |
242 | #endif |
243 | return Harness::Done; |
244 | } |
245 | |