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.
30typedef void (*TestFunctionType)(size_t);
31
32tbb::atomic<size_t> sum;
33
34// This function is called via parallel_for_each
35void TestFunction (size_t value) {
36 sum += (unsigned int)value;
37}
38
39const size_t NUMBER_OF_ELEMENTS = 1000;
40
41// Tests tbb::parallel_for_each functionality
42template <typename Iterator>
43void 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
64typedef void (*TestMutatorType)(size_t&);
65
66void 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.
73template <typename Iterator>
74void 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
91void test_function_with_exception(size_t) {
92 ThrowTestException();
93}
94
95template <typename Iterator>
96void 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
115void function_to_cancel(size_t ) {
116 ++g_CurExecuted;
117 CancellatorTask::WaitUntilReady();
118}
119
120template <typename Iterator>
121class 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 }
137public:
138 my_worker_pforeach_task ( tbb::task_group_context &ctx) : my_ctx(ctx) { }
139};
140
141template <typename Iterator>
142void 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
152const size_t elements = 10000;
153const size_t init_sum = 0;
154tbb::atomic<size_t> element_counter;
155
156template<size_t K>
157struct 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
167void 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
205int 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