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#define HARNESS_DEFAULT_MIN_THREADS 0
18#define HARNESS_DEFAULT_MAX_THREADS 4
19
20#include "tbb/enumerable_thread_specific.h"
21#include "tbb/task_scheduler_init.h"
22#include "tbb/parallel_for.h"
23#include "tbb/parallel_reduce.h"
24#include "tbb/blocked_range.h"
25#include "tbb/tick_count.h"
26#include "tbb/tbb_allocator.h"
27#include "tbb/tbb_thread.h"
28#include "tbb/atomic.h"
29
30#include <cstring>
31#include <vector>
32#include <deque>
33#include <list>
34#include <map>
35#include <utility>
36
37#include "harness_assert.h"
38#include "harness.h"
39#include "harness_checktype.h"
40
41#include "../tbbmalloc/shared_utils.h"
42using rml::internal::estimatedCacheLineSize;
43
44#if __TBB_GCC_WARNING_SUPPRESSION_PRESENT
45#pragma GCC diagnostic ignored "-Wuninitialized"
46#endif
47
48static tbb::atomic<int> construction_counter;
49static tbb::atomic<int> destruction_counter;
50
51#if TBB_USE_DEBUG
52const int REPETITIONS = 4;
53const int N = 10000;
54const int RANGE_MIN=1000;
55#else
56const int REPETITIONS = 10;
57const int N = 100000;
58const int RANGE_MIN=10000;
59#endif
60const int VALID_NUMBER_OF_KEYS = 100;
61const double EXPECTED_SUM = (REPETITIONS + 1) * N;
62
63//! A minimal class that occupies N bytes.
64/** Defines default and copy constructor, and allows implicit operator&.
65 Hides operator=. */
66template<size_t N=tbb::internal::NFS_MaxLineSize>
67class minimal: NoAssign {
68private:
69 int my_value;
70 bool is_constructed;
71 char pad[N-sizeof(int) - sizeof(bool)];
72public:
73 minimal() : NoAssign(), my_value(0) { ++construction_counter; is_constructed = true; }
74 minimal( const minimal &m ) : NoAssign(), my_value(m.my_value) { ++construction_counter; is_constructed = true; }
75 ~minimal() { ++destruction_counter; ASSERT(is_constructed, NULL); is_constructed = false; }
76 void set_value( const int i ) { ASSERT(is_constructed, NULL); my_value = i; }
77 int value( ) const { ASSERT(is_constructed, NULL); return my_value; }
78};
79
80static size_t AlignMask = 0; // set to cache-line-size - 1
81
82template<typename T>
83T& check_alignment(T& t, const char *aname) {
84 if( !tbb::internal::is_aligned(&t, AlignMask)) {
85 REPORT_ONCE("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
86 }
87 return t;
88}
89
90template<typename T>
91const T& check_alignment(const T& t, const char *aname) {
92 if( !tbb::internal::is_aligned(&t, AlignMask)) {
93 REPORT_ONCE("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
94 }
95 return t;
96}
97
98// Test constructors which throw. If an ETS constructor throws before completion,
99// the already-built objects are un-constructed. Do not call the destructor if
100// this occurs.
101
102static tbb::atomic<int> gThrowValue;
103static int targetThrowValue = 3;
104
105class Thrower {
106public:
107 Thrower() {
108#if TBB_USE_EXCEPTIONS
109 if(++gThrowValue == targetThrowValue) {
110 throw std::bad_alloc();
111 }
112#endif
113 }
114};
115
116// MyThrower field of ThrowingConstructor will throw after a certain number of
117// construction calls. The constructor unwinder wshould unconstruct the instance
118// of check_type<int> that was constructed just before.
119class ThrowingConstructor {
120 check_type<int> m_checktype;
121 Thrower m_throwing_field;
122public:
123 int m_cnt;
124 ThrowingConstructor() : m_checktype(), m_throwing_field() { m_cnt = 0;}
125private:
126};
127
128//
129// A helper class that simplifies writing the tests since minimal does not
130// define = or + operators.
131//
132
133template< typename T >
134struct test_helper {
135 static inline void init(T &e) { e = static_cast<T>(0); }
136 static inline void sum(T &e, const int addend ) { e += static_cast<T>(addend); }
137 static inline void sum(T &e, const double addend ) { e += static_cast<T>(addend); }
138 static inline void set(T &e, const int value ) { e = static_cast<T>(value); }
139 static inline double get(const T &e ) { return static_cast<double>(e); }
140};
141
142template<size_t N>
143struct test_helper<minimal<N> > {
144 static inline void init(minimal<N> &sum) { sum.set_value( 0 ); }
145 static inline void sum(minimal<N> &sum, const int addend ) { sum.set_value( sum.value() + addend); }
146 static inline void sum(minimal<N> &sum, const double addend ) { sum.set_value( sum.value() + static_cast<int>(addend)); }
147 static inline void sum(minimal<N> &sum, const minimal<N> &addend ) { sum.set_value( sum.value() + addend.value()); }
148 static inline void set(minimal<N> &v, const int value ) { v.set_value( static_cast<int>(value) ); }
149 static inline double get(const minimal<N> &sum ) { return static_cast<double>(sum.value()); }
150};
151
152template<>
153struct test_helper<ThrowingConstructor> {
154 static inline void init(ThrowingConstructor &sum) { sum.m_cnt = 0; }
155 static inline void sum(ThrowingConstructor &sum, const int addend ) { sum.m_cnt += addend; }
156 static inline void sum(ThrowingConstructor &sum, const double addend ) { sum.m_cnt += static_cast<int>(addend); }
157 static inline void sum(ThrowingConstructor &sum, const ThrowingConstructor &addend ) { sum.m_cnt += addend.m_cnt; }
158 static inline void set(ThrowingConstructor &v, const int value ) { v.m_cnt = static_cast<int>(value); }
159 static inline double get(const ThrowingConstructor &sum ) { return static_cast<double>(sum.m_cnt); }
160};
161
162//! Tag class used to make certain constructors hard to invoke accidentally.
163struct SecretTagType {} SecretTag;
164
165//// functors and routines for initialization and combine
166
167//! Counts instances of FunctorFinit
168static tbb::atomic<int> FinitCounter;
169
170template <typename T, int Value>
171struct FunctorFinit {
172 FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
173 FunctorFinit( SecretTagType ) {++FinitCounter;}
174 ~FunctorFinit() {--FinitCounter;}
175 T operator()() { return Value; }
176};
177
178template <int Value>
179struct FunctorFinit<ThrowingConstructor,Value> {
180 FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
181 FunctorFinit( SecretTagType ) {++FinitCounter;}
182 ~FunctorFinit() {--FinitCounter;}
183 ThrowingConstructor operator()() { ThrowingConstructor temp; temp.m_cnt = Value; return temp; }
184};
185
186template <size_t N, int Value>
187struct FunctorFinit<minimal<N>,Value> {
188 FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
189 FunctorFinit( SecretTagType ) {++FinitCounter;}
190 ~FunctorFinit() {--FinitCounter;}
191 minimal<N> operator()() {
192 minimal<N> result;
193 result.set_value( Value );
194 return result;
195 }
196};
197
198// Addition
199
200template <typename T>
201struct FunctorAddCombineRef {
202 T operator()(const T& left, const T& right) const {
203 return left+right;
204 }
205};
206
207template <size_t N>
208struct FunctorAddCombineRef<minimal<N> > {
209 minimal<N> operator()(const minimal<N>& left, const minimal<N>& right) const {
210 minimal<N> result;
211 result.set_value( left.value() + right.value() );
212 return result;
213 }
214};
215
216template <>
217struct FunctorAddCombineRef<ThrowingConstructor> {
218 ThrowingConstructor operator()(const ThrowingConstructor& left, const ThrowingConstructor& right) const {
219 ThrowingConstructor result;
220 result.m_cnt = ( left.m_cnt + right.m_cnt );
221 return result;
222 }
223};
224
225template <typename T>
226struct FunctorAddCombine {
227 T operator()(T left, T right ) const {
228 return FunctorAddCombineRef<T>()( left, right );
229 }
230};
231
232template <typename T>
233T FunctionAddByRef( const T &left, const T &right) {
234 return FunctorAddCombineRef<T>()( left, right );
235}
236
237template <typename T>
238T FunctionAdd( T left, T right) { return FunctionAddByRef(left,right); }
239
240template <typename T>
241class Accumulator {
242public:
243 Accumulator(T& _result) : my_result(_result) {}
244 Accumulator& operator=(const Accumulator& other) {
245 test_helper<T>::set(my_result, test_helper<T>::get(other));
246 return *this;
247 }
248 void operator()(const T& new_bit) { test_helper<T>::sum(my_result, new_bit); }
249private:
250 T& my_result;
251};
252
253template <typename T>
254class ClearingAccumulator {
255public:
256 ClearingAccumulator(T& _result) : my_result(_result) {}
257 ClearingAccumulator& operator=(const ClearingAccumulator& other) {
258 test_helper<T>::set(my_result, test_helper<T>::get(other));
259 return *this;
260 }
261 void operator()(T& new_bit) {
262 test_helper<T>::sum(my_result, new_bit);
263 test_helper<T>::init(new_bit);
264 }
265 static void AssertClean(const T& thread_local_value) {
266 T zero;
267 test_helper<T>::init(zero);
268 ASSERT(test_helper<T>::get(thread_local_value)==test_helper<T>::get(zero),
269 "combine_each does not allow to modify thread local values?");
270 }
271private:
272 T& my_result;
273};
274
275//// end functors and routines
276
277template< typename T >
278void run_serial_scalar_tests(const char *test_name) {
279 tbb::tick_count t0;
280 T sum;
281 test_helper<T>::init(sum);
282
283 REMARK("Testing serial %s... ", test_name);
284 for (int t = -1; t < REPETITIONS; ++t) {
285 if (Verbose && t == 0) t0 = tbb::tick_count::now();
286 for (int i = 0; i < N; ++i) {
287 test_helper<T>::sum(sum,1);
288 }
289 }
290
291 double result_value = test_helper<T>::get(sum);
292 ASSERT( EXPECTED_SUM == result_value, NULL);
293 REMARK("done\nserial %s, 0, %g, %g\n", test_name, result_value, ( tbb::tick_count::now() - t0).seconds());
294}
295
296
297template <typename T, template<class> class Allocator>
298class parallel_scalar_body: NoAssign {
299 typedef tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
300 ets_type &sums;
301 const char* allocator_name;
302
303public:
304
305 parallel_scalar_body ( ets_type &_sums, const char *alloc_name ) : sums(_sums), allocator_name(alloc_name) { }
306
307 void operator()( const tbb::blocked_range<int> &r ) const {
308 for (int i = r.begin(); i != r.end(); ++i)
309 test_helper<T>::sum( check_alignment(sums.local(),allocator_name), 1 );
310 }
311
312};
313
314template< typename T, template<class> class Allocator>
315void run_parallel_scalar_tests_nocombine(const char *test_name, const char *allocator_name) {
316
317 typedef tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
318
319 Check<T> my_check;
320 gThrowValue = 0;
321 {
322 // We assume that static_sums zero-initialized or has a default constructor that zeros it.
323 static ets_type static_sums = ets_type( T() );
324
325 T exemplar;
326 test_helper<T>::init(exemplar);
327
328 for (int p = MinThread; p <= MaxThread; ++p) {
329 REMARK("Testing parallel %s with allocator %s on %d thread(s)... ", test_name, allocator_name, p);
330 tbb::task_scheduler_init init(p);
331 tbb::tick_count t0;
332
333 T iterator_sum;
334 test_helper<T>::init(iterator_sum);
335
336 T finit_ets_sum;
337 test_helper<T>::init(finit_ets_sum);
338
339 T const_iterator_sum;
340 test_helper<T>::init(const_iterator_sum);
341
342 T range_sum;
343 test_helper<T>::init(range_sum);
344
345 T const_range_sum;
346 test_helper<T>::init(const_range_sum);
347
348 T cconst_sum;
349 test_helper<T>::init(cconst_sum);
350
351 T assign_sum;
352 test_helper<T>::init(assign_sum);
353
354 T cassgn_sum;
355 test_helper<T>::init(cassgn_sum);
356 T non_cassgn_sum;
357 test_helper<T>::init(non_cassgn_sum);
358
359 T static_sum;
360 test_helper<T>::init(static_sum);
361
362 for (int t = -1; t < REPETITIONS; ++t) {
363 if (Verbose && t == 0) t0 = tbb::tick_count::now();
364
365 static_sums.clear();
366
367 ets_type sums(exemplar);
368 FunctorFinit<T,0> my_finit(SecretTag);
369 ets_type finit_ets(my_finit);
370
371 ASSERT( sums.empty(), NULL);
372 tbb::parallel_for( tbb::blocked_range<int>( 0, N, RANGE_MIN ), parallel_scalar_body<T,Allocator>( sums, allocator_name ) );
373 ASSERT( !sums.empty(), NULL);
374
375 ASSERT( finit_ets.empty(), NULL);
376 tbb::parallel_for( tbb::blocked_range<int>( 0, N, RANGE_MIN ), parallel_scalar_body<T,Allocator>( finit_ets, allocator_name ) );
377 ASSERT( !finit_ets.empty(), NULL);
378
379 ASSERT(static_sums.empty(), NULL);
380 tbb::parallel_for( tbb::blocked_range<int>( 0, N, RANGE_MIN ), parallel_scalar_body<T,Allocator>( static_sums, allocator_name ) );
381 ASSERT( !static_sums.empty(), NULL);
382
383 // use iterator
384 typename ets_type::size_type size = 0;
385 for ( typename ets_type::iterator i = sums.begin(); i != sums.end(); ++i ) {
386 ++size;
387 test_helper<T>::sum(iterator_sum, *i);
388 }
389 ASSERT( sums.size() == size, NULL);
390
391 // use const_iterator
392 for ( typename ets_type::const_iterator i = sums.begin(); i != sums.end(); ++i ) {
393 test_helper<T>::sum(const_iterator_sum, *i);
394 }
395
396 // use range_type
397 typename ets_type::range_type r = sums.range();
398 for ( typename ets_type::range_type::const_iterator i = r.begin(); i != r.end(); ++i ) {
399 test_helper<T>::sum(range_sum, *i);
400 }
401
402 // use const_range_type
403 typename ets_type::const_range_type cr = sums.range();
404 for ( typename ets_type::const_range_type::iterator i = cr.begin(); i != cr.end(); ++i ) {
405 test_helper<T>::sum(const_range_sum, *i);
406 }
407
408 // test copy constructor, with TLS-cached locals
409 typedef typename tbb::enumerable_thread_specific<T, Allocator<T>, tbb::ets_key_per_instance> cached_ets_type;
410
411 cached_ets_type cconst(sums);
412
413 for ( typename cached_ets_type::const_iterator i = cconst.begin(); i != cconst.end(); ++i ) {
414 test_helper<T>::sum(cconst_sum, *i);
415 }
416
417 // test assignment
418 ets_type assigned;
419 assigned = sums;
420
421 for ( typename ets_type::const_iterator i = assigned.begin(); i != assigned.end(); ++i ) {
422 test_helper<T>::sum(assign_sum, *i);
423 }
424
425 // test assign to and from cached locals
426 cached_ets_type cassgn;
427 cassgn = sums;
428 for ( typename cached_ets_type::const_iterator i = cassgn.begin(); i != cassgn.end(); ++i ) {
429 test_helper<T>::sum(cassgn_sum, *i);
430 }
431
432 ets_type non_cassgn;
433 non_cassgn = cassgn;
434 for ( typename ets_type::const_iterator i = non_cassgn.begin(); i != non_cassgn.end(); ++i ) {
435 test_helper<T>::sum(non_cassgn_sum, *i);
436 }
437
438 // test finit-initialized ets
439 for(typename ets_type::const_iterator i = finit_ets.begin(); i != finit_ets.end(); ++i) {
440 test_helper<T>::sum(finit_ets_sum, *i);
441 }
442
443 // test static ets
444 for(typename ets_type::const_iterator i = static_sums.begin(); i != static_sums.end(); ++i) {
445 test_helper<T>::sum(static_sum, *i);
446 }
447
448 }
449
450 ASSERT( EXPECTED_SUM == test_helper<T>::get(iterator_sum), NULL);
451 ASSERT( EXPECTED_SUM == test_helper<T>::get(const_iterator_sum), NULL);
452 ASSERT( EXPECTED_SUM == test_helper<T>::get(range_sum), NULL);
453 ASSERT( EXPECTED_SUM == test_helper<T>::get(const_range_sum), NULL);
454
455 ASSERT( EXPECTED_SUM == test_helper<T>::get(cconst_sum), NULL);
456 ASSERT( EXPECTED_SUM == test_helper<T>::get(assign_sum), NULL);
457 ASSERT( EXPECTED_SUM == test_helper<T>::get(cassgn_sum), NULL);
458 ASSERT( EXPECTED_SUM == test_helper<T>::get(non_cassgn_sum), NULL);
459 ASSERT( EXPECTED_SUM == test_helper<T>::get(finit_ets_sum), NULL);
460 ASSERT( EXPECTED_SUM == test_helper<T>::get(static_sum), NULL);
461
462 REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, test_helper<T>::get(iterator_sum),
463 ( tbb::tick_count::now() - t0).seconds());
464 }
465 } // Check block
466}
467
468template< typename T, template<class> class Allocator>
469void run_parallel_scalar_tests(const char *test_name, const char *allocator_name) {
470
471 typedef tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
472 bool exception_caught = false;
473
474 // We assume that static_sums zero-initialized or has a default constructor that zeros it.
475 static ets_type static_sums = ets_type( T() );
476
477 T exemplar;
478 test_helper<T>::init(exemplar);
479
480 int test_throw_count = 10;
481 // the test will be performed repeatedly until it does not throw. For non-throwing types
482 // this means once; for the throwing type test it may loop two or three times. The
483 // value of targetThrowValue will determine when and if the test will throw.
484 do {
485 targetThrowValue = test_throw_count; // keep testing until we get no exception
486 exception_caught = false;
487#if TBB_USE_EXCEPTIONS
488 try {
489#endif
490 run_parallel_scalar_tests_nocombine<T,Allocator>(test_name, allocator_name);
491#if TBB_USE_EXCEPTIONS
492 }
493 catch(...) {
494 REMARK("Exception caught %d\n", targetThrowValue);
495 }
496#endif
497 for (int p = MinThread; p <= MaxThread; ++p) {
498 REMARK("Testing parallel %s with allocator %s on %d thread(s)... ", test_name, allocator_name, p);
499 tbb::task_scheduler_init init(p);
500 tbb::tick_count t0;
501
502 gThrowValue = 0;
503
504 T combine_sum;
505 test_helper<T>::init(combine_sum);
506
507 T combine_ref_sum;
508 test_helper<T>::init(combine_ref_sum);
509
510 T accumulator_sum;
511 test_helper<T>::init(accumulator_sum);
512
513 T static_sum;
514 test_helper<T>::init(static_sum);
515
516 T clearing_accumulator_sum;
517 test_helper<T>::init(clearing_accumulator_sum);
518
519 {
520 Check<T> my_check;
521#if TBB_USE_EXCEPTIONS
522 try
523#endif
524 {
525 for (int t = -1; t < REPETITIONS; ++t) {
526 if (Verbose && t == 0) t0 = tbb::tick_count::now();
527
528 static_sums.clear();
529
530 ets_type sums(exemplar);
531
532 ASSERT( sums.empty(), NULL);
533 tbb::parallel_for( tbb::blocked_range<int>( 0, N, RANGE_MIN ),
534 parallel_scalar_body<T,Allocator>( sums, allocator_name ) );
535 ASSERT( !sums.empty(), NULL);
536
537 ASSERT(static_sums.empty(), NULL);
538 tbb::parallel_for( tbb::blocked_range<int>( 0, N, RANGE_MIN ),
539 parallel_scalar_body<T,Allocator>( static_sums, allocator_name ) );
540 ASSERT( !static_sums.empty(), NULL);
541
542 // Use combine
543 test_helper<T>::sum(combine_sum, sums.combine(FunctionAdd<T>));
544 test_helper<T>::sum(combine_ref_sum, sums.combine(FunctionAddByRef<T>));
545 test_helper<T>::sum(static_sum, static_sums.combine(FunctionAdd<T>));
546
547 // Accumulate with combine_each
548 sums.combine_each(Accumulator<T>(accumulator_sum));
549 // Accumulate and clear thread-local values
550 sums.combine_each(ClearingAccumulator<T>(clearing_accumulator_sum));
551 // Check that the values were cleared
552 sums.combine_each(ClearingAccumulator<T>::AssertClean);
553 }
554 }
555#if TBB_USE_EXCEPTIONS
556 catch(...) {
557 REMARK("Exception caught %d\n", targetThrowValue);
558 exception_caught = true;
559 }
560#endif
561 }
562
563 ASSERT( EXPECTED_SUM == test_helper<T>::get(combine_sum) || exception_caught, NULL);
564 ASSERT( EXPECTED_SUM == test_helper<T>::get(combine_ref_sum) || exception_caught, NULL);
565 ASSERT( EXPECTED_SUM == test_helper<T>::get(static_sum) || exception_caught, NULL);
566 ASSERT( EXPECTED_SUM == test_helper<T>::get(accumulator_sum) || exception_caught, NULL);
567 ASSERT( EXPECTED_SUM == test_helper<T>::get(clearing_accumulator_sum) || exception_caught, NULL);
568
569 REMARK("done\nparallel combine %s, %d, %g, %g\n", test_name, p, test_helper<T>::get(combine_sum),
570 ( tbb::tick_count::now() - t0).seconds());
571 } // MinThread .. MaxThread
572 test_throw_count += 10; // keep testing until we don't get an exception
573 } while (exception_caught && test_throw_count < 200);
574 ASSERT(!exception_caught, "No non-exception test completed");
575}
576
577template <typename T, template<class> class Allocator>
578class parallel_vector_for_body: NoAssign {
579 typedef std::vector<T, tbb::tbb_allocator<T> > container_type;
580 typedef tbb::enumerable_thread_specific< container_type, Allocator<container_type> > ets_type;
581 ets_type &locals;
582 const char *allocator_name;
583
584public:
585
586 parallel_vector_for_body ( ets_type &_locals, const char *aname ) : locals(_locals), allocator_name(aname) { }
587
588 void operator()( const tbb::blocked_range<int> &r ) const {
589 T one;
590 test_helper<T>::set(one, 1);
591
592 for (int i = r.begin(); i < r.end(); ++i) {
593 check_alignment(locals.local(),allocator_name).push_back( one );
594 }
595 }
596
597};
598
599template <typename R, typename T>
600struct parallel_vector_reduce_body {
601
602 T sum;
603 size_t count;
604 typedef std::vector<T, tbb::tbb_allocator<T> > container_type;
605
606 parallel_vector_reduce_body ( ) : count(0) { test_helper<T>::init(sum); }
607 parallel_vector_reduce_body ( parallel_vector_reduce_body<R, T> &, tbb::split ) : count(0) { test_helper<T>::init(sum); }
608
609 void operator()( const R &r ) {
610 for (typename R::iterator ri = r.begin(); ri != r.end(); ++ri) {
611 const container_type &v = *ri;
612 ++count;
613 for (typename container_type::const_iterator vi = v.begin(); vi != v.end(); ++vi) {
614 test_helper<T>::sum(sum, *vi);
615 }
616 }
617 }
618
619 void join( const parallel_vector_reduce_body &b ) {
620 test_helper<T>::sum(sum,b.sum);
621 count += b.count;
622 }
623
624};
625
626template< typename T, template<class> class Allocator>
627void run_parallel_vector_tests(const char *test_name, const char *allocator_name) {
628 tbb::tick_count t0;
629 typedef std::vector<T, tbb::tbb_allocator<T> > container_type;
630 typedef tbb::enumerable_thread_specific< container_type, Allocator<container_type> > ets_type;
631
632 for (int p = MinThread; p <= MaxThread; ++p) {
633 REMARK("Testing parallel %s with allocator %s on %d thread(s)... ", test_name, allocator_name, p);
634 tbb::task_scheduler_init init(p);
635
636 T sum;
637 test_helper<T>::init(sum);
638
639 for (int t = -1; t < REPETITIONS; ++t) {
640 if (Verbose && t == 0) t0 = tbb::tick_count::now();
641 ets_type vs;
642
643 ASSERT( vs.empty(), NULL );
644 tbb::parallel_for( tbb::blocked_range<int> (0, N, RANGE_MIN),
645 parallel_vector_for_body<T,Allocator>( vs, allocator_name ) );
646 ASSERT( !vs.empty(), NULL );
647
648 // copy construct
649 ets_type vs2(vs); // this causes an assertion failure, related to allocators...
650
651 // assign
652 ets_type vs3;
653 vs3 = vs;
654
655 parallel_vector_reduce_body< typename ets_type::const_range_type, T > pvrb;
656 tbb::parallel_reduce ( vs.range(1), pvrb );
657
658 test_helper<T>::sum(sum, pvrb.sum);
659
660 ASSERT( vs.size() == pvrb.count, NULL );
661 ASSERT( vs2.size() == pvrb.count, NULL );
662 ASSERT( vs3.size() == pvrb.count, NULL );
663
664 tbb::flattened2d<ets_type> fvs = flatten2d(vs);
665 size_t ccount = fvs.size();
666 ASSERT( ccount == size_t(N), NULL );
667 size_t elem_cnt = 0;
668 for(typename tbb::flattened2d<ets_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
669 ++elem_cnt;
670 };
671 ASSERT( ccount == elem_cnt, NULL );
672
673 elem_cnt = 0;
674 for(typename tbb::flattened2d<ets_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) {
675 ++elem_cnt;
676 };
677 ASSERT( ccount == elem_cnt, NULL );
678
679#if __TBB_ETS_USE_CPP11
680 // Test the ETS constructor with multiple args
681 T minus_one;
682 test_helper<T>::set(minus_one, -1);
683 // Set ETS to construct "local" vectors pre-occupied with 25 "minus_one"s
684 // Cast 25 to size_type to prevent Intel Compiler SFINAE compilation issues with gcc 5.
685 ets_type vvs( typename container_type::size_type(25), minus_one, tbb::tbb_allocator<T>() );
686 ASSERT( vvs.empty(), NULL );
687 tbb::parallel_for ( tbb::blocked_range<int> (0, N, RANGE_MIN), parallel_vector_for_body<T,Allocator>( vvs, allocator_name ) );
688 ASSERT( !vvs.empty(), NULL );
689
690 parallel_vector_reduce_body< typename ets_type::const_range_type, T > pvrb2;
691 tbb::parallel_reduce ( vvs.range(1), pvrb2 );
692 ASSERT( pvrb2.count == vvs.size(), NULL );
693 ASSERT( test_helper<T>::get(pvrb2.sum) == N-pvrb2.count*25, NULL );
694
695 tbb::flattened2d<ets_type> fvvs = flatten2d(vvs);
696 ccount = fvvs.size();
697 ASSERT( ccount == N+pvrb2.count*25, NULL );
698#endif
699 }
700
701 double result_value = test_helper<T>::get(sum);
702 ASSERT( EXPECTED_SUM == result_value, NULL);
703 REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, result_value, ( tbb::tick_count::now() - t0).seconds());
704 }
705}
706
707template<typename T, template<class> class Allocator>
708void run_cross_type_vector_tests(const char *test_name) {
709 tbb::tick_count t0;
710 const char* allocator_name = "default";
711 typedef std::vector<T, tbb::tbb_allocator<T> > container_type;
712
713 for (int p = MinThread; p <= MaxThread; ++p) {
714 REMARK("Testing parallel %s on %d thread(s)... ", test_name, p);
715 tbb::task_scheduler_init init(p);
716
717 T sum;
718 test_helper<T>::init(sum);
719
720 for (int t = -1; t < REPETITIONS; ++t) {
721 if (Verbose && t == 0) t0 = tbb::tick_count::now();
722 typedef typename tbb::enumerable_thread_specific< container_type, Allocator<container_type>, tbb::ets_no_key > ets_nokey_type;
723 typedef typename tbb::enumerable_thread_specific< container_type, Allocator<container_type>, tbb::ets_key_per_instance > ets_tlskey_type;
724 ets_nokey_type vs;
725
726 ASSERT( vs.empty(), NULL);
727 tbb::parallel_for ( tbb::blocked_range<int> (0, N, RANGE_MIN), parallel_vector_for_body<T, Allocator>( vs, allocator_name ) );
728 ASSERT( !vs.empty(), NULL);
729
730 // copy construct
731 ets_tlskey_type vs2(vs);
732
733 // assign
734 ets_nokey_type vs3;
735 vs3 = vs2;
736
737 parallel_vector_reduce_body< typename ets_nokey_type::const_range_type, T > pvrb;
738 tbb::parallel_reduce ( vs3.range(1), pvrb );
739
740 test_helper<T>::sum(sum, pvrb.sum);
741
742 ASSERT( vs3.size() == pvrb.count, NULL);
743
744 tbb::flattened2d<ets_nokey_type> fvs = flatten2d(vs3);
745 size_t ccount = fvs.size();
746 size_t elem_cnt = 0;
747 for(typename tbb::flattened2d<ets_nokey_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
748 ++elem_cnt;
749 };
750 ASSERT(ccount == elem_cnt, NULL);
751
752 elem_cnt = 0;
753 for(typename tbb::flattened2d<ets_nokey_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) {
754 ++elem_cnt;
755 };
756 ASSERT(ccount == elem_cnt, NULL);
757 }
758
759 double result_value = test_helper<T>::get(sum);
760 ASSERT( EXPECTED_SUM == result_value, NULL);
761 REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, result_value, ( tbb::tick_count::now() - t0).seconds());
762 }
763}
764
765template< typename T >
766void run_serial_vector_tests(const char *test_name) {
767 tbb::tick_count t0;
768 T sum;
769 test_helper<T>::init(sum);
770 T one;
771 test_helper<T>::set(one, 1);
772
773 REMARK("Testing serial %s... ", test_name);
774 for (int t = -1; t < REPETITIONS; ++t) {
775 if (Verbose && t == 0) t0 = tbb::tick_count::now();
776 std::vector<T, tbb::tbb_allocator<T> > v;
777 for (int i = 0; i < N; ++i) {
778 v.push_back( one );
779 }
780 for (typename std::vector<T, tbb::tbb_allocator<T> >::const_iterator i = v.begin(); i != v.end(); ++i)
781 test_helper<T>::sum(sum, *i);
782 }
783
784 double result_value = test_helper<T>::get(sum);
785 ASSERT( EXPECTED_SUM == result_value, NULL);
786 REMARK("done\nserial %s, 0, %g, %g\n", test_name, result_value, ( tbb::tick_count::now() - t0).seconds());
787}
788
789const size_t line_size = tbb::internal::NFS_MaxLineSize;
790
791void run_serial_tests() {
792 run_serial_scalar_tests<int>("int");
793 run_serial_scalar_tests<double>("double");
794 run_serial_scalar_tests<minimal<> >("minimal<>");
795 run_serial_vector_tests<int>("std::vector<int, tbb::tbb_allocator<int> >");
796 run_serial_vector_tests<double>("std::vector<double, tbb::tbb_allocator<double> >");
797}
798
799template<template<class>class Allocator>
800void run_parallel_tests(const char *allocator_name) {
801 run_parallel_scalar_tests<int, Allocator>("int",allocator_name);
802 run_parallel_scalar_tests<double, Allocator>("double",allocator_name);
803 run_parallel_scalar_tests_nocombine<minimal<>,Allocator>("minimal<>",allocator_name);
804 run_parallel_scalar_tests<ThrowingConstructor, Allocator>("ThrowingConstructor", allocator_name);
805 run_parallel_vector_tests<int, Allocator>("std::vector<int, tbb::tbb_allocator<int> >",allocator_name);
806 run_parallel_vector_tests<double, Allocator>("std::vector<double, tbb::tbb_allocator<double> >",allocator_name);
807}
808
809void run_cross_type_tests() {
810 // cross-type scalar tests are part of run_parallel_scalar_tests_nocombine
811 run_cross_type_vector_tests<int, tbb::tbb_allocator>("std::vector<int, tbb::tbb_allocator<int> >");
812 run_cross_type_vector_tests<double, tbb::tbb_allocator>("std::vector<double, tbb::tbb_allocator<double> >");
813}
814
815typedef tbb::enumerable_thread_specific<minimal<line_size> > flogged_ets;
816
817class set_body {
818 flogged_ets *a;
819
820public:
821 set_body( flogged_ets*_a ) : a(_a) { }
822
823 void operator() ( ) const {
824 for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
825 check_alignment(a[i].local(), "default").set_value(i + 1);
826 }
827 }
828
829};
830
831void do_tbb_threads( int max_threads, flogged_ets a[] ) {
832 std::vector< tbb::tbb_thread * > threads;
833
834 for (int p = 0; p < max_threads; ++p) {
835 threads.push_back( new tbb::tbb_thread ( set_body( a ) ) );
836 }
837
838 for (int p = 0; p < max_threads; ++p) {
839 threads[p]->join();
840 }
841
842 for(int p = 0; p < max_threads; ++p) {
843 delete threads[p];
844 }
845}
846
847void flog_key_creation_and_deletion() {
848 const int FLOG_REPETITIONS = 100;
849
850 for (int p = MinThread; p <= MaxThread; ++p) {
851 REMARK("Testing repeated deletes on %d threads... ", p);
852
853 for (int j = 0; j < FLOG_REPETITIONS; ++j) {
854 construction_counter = 0;
855 destruction_counter = 0;
856
857 // causes VALID_NUMBER_OF_KEYS exemplar instances to be constructed
858 flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS];
859 ASSERT(int(construction_counter) == 0, NULL); // no exemplars or actual locals have been constructed
860 ASSERT(int(destruction_counter) == 0, NULL); // and none have been destroyed
861
862 // causes p * VALID_NUMBER_OF_KEYS minimals to be created
863 do_tbb_threads(p, a);
864
865 for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
866 int pcnt = 0;
867 for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) {
868 ASSERT( (*tli).value() == i+1, NULL );
869 ++pcnt;
870 }
871 ASSERT( pcnt == p, NULL); // should be one local per thread.
872 }
873 delete[] a;
874 }
875
876 ASSERT( int(construction_counter) == (p)*VALID_NUMBER_OF_KEYS, NULL );
877 ASSERT( int(destruction_counter) == (p)*VALID_NUMBER_OF_KEYS, NULL );
878
879 REMARK("done\nTesting repeated clears on %d threads... ", p);
880
881 construction_counter = 0;
882 destruction_counter = 0;
883
884 // causes VALID_NUMBER_OF_KEYS exemplar instances to be constructed
885 flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS];
886
887 for (int j = 0; j < FLOG_REPETITIONS; ++j) {
888
889 // causes p * VALID_NUMBER_OF_KEYS minimals to be created
890 do_tbb_threads(p, a);
891
892 for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
893 for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) {
894 ASSERT( (*tli).value() == i+1, NULL );
895 }
896 a[i].clear();
897 ASSERT( static_cast<int>(a[i].end() - a[i].begin()) == 0, NULL );
898 }
899
900 }
901
902 delete[] a;
903
904 ASSERT( int(construction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS, NULL );
905 ASSERT( int(destruction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS, NULL );
906
907 REMARK("done\n");
908 }
909
910}
911
912template <typename inner_container>
913void flog_segmented_interator() {
914
915 bool found_error = false;
916 typedef typename inner_container::value_type T;
917 typedef std::vector< inner_container > nested_vec;
918 inner_container my_inner_container;
919 my_inner_container.clear();
920 nested_vec my_vec;
921
922 // simple nested vector (neither level empty)
923 const int maxval = 10;
924 for(int i=0; i < maxval; i++) {
925 my_vec.push_back(my_inner_container);
926 for(int j = 0; j < maxval; j++) {
927 my_vec.at(i).push_back((T)(maxval * i + j));
928 }
929 }
930
931 tbb::internal::segmented_iterator<nested_vec, T> my_si(my_vec);
932
933 T ii;
934 for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
935 if((*my_si) != ii) {
936 found_error = true;
937 REMARK( "*my_si=%d\n", int(*my_si));
938 }
939 }
940
941 // outer level empty
942 my_vec.clear();
943 for(my_si=my_vec.begin(); my_si != my_vec.end(); ++my_si) {
944 found_error = true;
945 }
946
947 // inner levels empty
948 my_vec.clear();
949 for(int i =0; i < maxval; ++i) {
950 my_vec.push_back(my_inner_container);
951 }
952 for(my_si = my_vec.begin(); my_si != my_vec.end(); ++my_si) {
953 found_error = true;
954 }
955
956 // every other inner container is empty
957 my_vec.clear();
958 for(int i=0; i < maxval; ++i) {
959 my_vec.push_back(my_inner_container);
960 if(i%2) {
961 for(int j = 0; j < maxval; ++j) {
962 my_vec.at(i).push_back((T)(maxval * (i/2) + j));
963 }
964 }
965 }
966 for(my_si = my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
967 if((*my_si) != ii) {
968 found_error = true;
969 REMARK("*my_si=%d, ii=%d\n", (int)(*my_si), (int)ii);
970 }
971 }
972
973 tbb::internal::segmented_iterator<nested_vec, const T> my_csi(my_vec);
974 for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
975 if((*my_csi) != ii) {
976 found_error = true;
977 REMARK( "*my_csi=%d\n", int(*my_csi));
978 }
979 }
980
981 // outer level empty
982 my_vec.clear();
983 for(my_csi=my_vec.begin(); my_csi != my_vec.end(); ++my_csi) {
984 found_error = true;
985 }
986
987 // inner levels empty
988 my_vec.clear();
989 for(int i =0; i < maxval; ++i) {
990 my_vec.push_back(my_inner_container);
991 }
992 for(my_csi = my_vec.begin(); my_csi != my_vec.end(); ++my_csi) {
993 found_error = true;
994 }
995
996 // every other inner container is empty
997 my_vec.clear();
998 for(int i=0; i < maxval; ++i) {
999 my_vec.push_back(my_inner_container);
1000 if(i%2) {
1001 for(int j = 0; j < maxval; ++j) {
1002 my_vec.at(i).push_back((T)(maxval * (i/2) + j));
1003 }
1004 }
1005 }
1006 for(my_csi = my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
1007 if((*my_csi) != ii) {
1008 found_error = true;
1009 REMARK("*my_csi=%d, ii=%d\n", (int)(*my_csi), (int)ii);
1010 }
1011 }
1012
1013
1014 if(found_error) REPORT("segmented_iterator failed\n");
1015}
1016
1017template <typename Key, typename Val>
1018void flog_segmented_iterator_map() {
1019 typedef typename std::map<Key, Val> my_map;
1020 typedef std::vector< my_map > nested_vec;
1021 my_map my_inner_container;
1022 my_inner_container.clear();
1023 nested_vec my_vec;
1024 my_vec.clear();
1025 bool found_error = false;
1026
1027 // simple nested vector (neither level empty)
1028 const int maxval = 4;
1029 for(int i=0; i < maxval; i++) {
1030 my_vec.push_back(my_inner_container);
1031 for(int j = 0; j < maxval; j++) {
1032 my_vec.at(i).insert(std::make_pair<Key,Val>(maxval * i + j, 2*(maxval*i + j)));
1033 }
1034 }
1035
1036 tbb::internal::segmented_iterator<nested_vec, std::pair<const Key, Val> > my_si(my_vec);
1037 Key ii;
1038 for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
1039 if(((*my_si).first != ii) || ((*my_si).second != 2*ii)) {
1040 found_error = true;
1041 REMARK( "ii=%d, (*my_si).first=%d, second=%d\n",ii, int((*my_si).first), int((*my_si).second));
1042 }
1043 }
1044
1045 tbb::internal::segmented_iterator<nested_vec, const std::pair<const Key, Val> > my_csi(my_vec);
1046 for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
1047 if(((*my_csi).first != ii) || ((*my_csi).second != 2*ii)) {
1048 found_error = true;
1049 REMARK( "ii=%d, (*my_csi).first=%d, second=%d\n",ii, int((*my_csi).first), int((*my_csi).second));
1050 }
1051 }
1052 if(found_error) REPORT("segmented_iterator_map failed\n");
1053}
1054
1055void run_segmented_iterator_tests() {
1056 // only the following containers can be used with the segmented iterator.
1057 REMARK("Running Segmented Iterator Tests\n");
1058 flog_segmented_interator<std::vector< int > >();
1059 flog_segmented_interator<std::vector< double > >();
1060 flog_segmented_interator<std::deque< int > >();
1061 flog_segmented_interator<std::deque< double > >();
1062 flog_segmented_interator<std::list< int > >();
1063 flog_segmented_interator<std::list< double > >();
1064
1065 flog_segmented_iterator_map<int, int>();
1066 flog_segmented_iterator_map<int, double>();
1067}
1068
1069template<typename T, template<class> class Allocator, typename Init>
1070tbb::enumerable_thread_specific<T,Allocator<T> > MakeETS( Init init ) {
1071 return tbb::enumerable_thread_specific<T,Allocator<T> >(init);
1072}
1073#if __TBB_ETS_USE_CPP11
1074// In some GCC versions, parameter packs in lambdas might cause compile errors
1075template<typename ETS, typename... P>
1076struct MakeETS_Functor {
1077 ETS operator()( typename tbb::internal::strip<P>::type&&... params ) {
1078 return ETS(std::move(params)...);
1079 }
1080};
1081template<typename T, template<class> class Allocator, typename... P>
1082tbb::enumerable_thread_specific<T,Allocator<T> > MakeETS( tbb::internal::stored_pack<P...> pack ) {
1083 typedef tbb::enumerable_thread_specific<T,Allocator<T> > result_type;
1084 return tbb::internal::call_and_return< result_type >(
1085 MakeETS_Functor<result_type,P...>(), std::move(pack)
1086 );
1087}
1088#endif
1089
1090template<typename T, template<class> class Allocator, typename InitSrc, typename InitDst, typename Validator>
1091void ets_copy_assign_test( InitSrc init1, InitDst init2, Validator check, const char *allocator_name ) {
1092 typedef tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
1093
1094 // Create the source instance
1095 const ets_type& cref_binder = MakeETS<T, Allocator>(init1);
1096 ets_type& source = const_cast<ets_type&>(cref_binder);
1097 check(check_alignment(source.local(),allocator_name));
1098
1099 // Test copy construction
1100 bool existed = false;
1101 ets_type copy(source);
1102 check(check_alignment(copy.local(existed),allocator_name));
1103 ASSERT(existed, "Local data not created by ETS copy constructor");
1104 copy.clear();
1105 check(check_alignment(copy.local(),allocator_name));
1106
1107 // Test assignment
1108 existed = false;
1109 ets_type assign(init2);
1110 assign = source;
1111 check(check_alignment(assign.local(existed),allocator_name));
1112 ASSERT(existed, "Local data not created by ETS assignment");
1113 assign.clear();
1114 check(check_alignment(assign.local(),allocator_name));
1115
1116#if __TBB_ETS_USE_CPP11
1117 // Create the source instance
1118 ets_type&& rvref_binder = MakeETS<T, Allocator>(init1);
1119 check(check_alignment(rvref_binder.local(),allocator_name));
1120
1121 // Test move construction
1122 existed = false;
1123 ets_type moved(rvref_binder);
1124 check(check_alignment(moved.local(existed),allocator_name));
1125 ASSERT(existed, "Local data not created by ETS move constructor");
1126 moved.clear();
1127 check(check_alignment(moved.local(),allocator_name));
1128
1129 // Test assignment
1130 existed = false;
1131 ets_type move_assign(init2);
1132 move_assign = std::move(moved);
1133 check(check_alignment(move_assign.local(existed),allocator_name));
1134 ASSERT(existed, "Local data not created by ETS move assignment");
1135 move_assign.clear();
1136 check(check_alignment(move_assign.local(),allocator_name));
1137#endif
1138}
1139
1140template<typename T, int Expected>
1141struct Validator {
1142 void operator()( const T& value ) {
1143 ASSERT(test_helper<T>::get(value) == Expected, NULL);
1144 }
1145 void operator()( const std::pair<int,T>& value ) {
1146 ASSERT(value.first > 0, NULL);
1147 ASSERT(test_helper<T>::get(value.second) == Expected*value.first, NULL);
1148 }
1149};
1150
1151template <typename T, template<class> class Allocator>
1152void run_assign_and_copy_constructor_test(const char *test_name, const char *allocator_name) {
1153 REMARK("Testing assignment and copy construction for %s with allocator %s\n", test_name, allocator_name);
1154 #define EXPECTED 3142
1155
1156 // test with exemplar initializer
1157 T src_init;
1158 test_helper<T>::set(src_init,EXPECTED);
1159 T other_init;
1160 test_helper<T>::init(other_init);
1161 ets_copy_assign_test<T, Allocator>(src_init, other_init, Validator<T,EXPECTED>(), allocator_name);
1162
1163 // test with function initializer
1164 FunctorFinit<T,EXPECTED> src_finit(SecretTag);
1165 FunctorFinit<T,0> other_finit(SecretTag);
1166 ets_copy_assign_test<T, Allocator>(src_finit, other_finit, Validator<T,EXPECTED>(), allocator_name);
1167
1168#if __TBB_ETS_USE_CPP11
1169 // test with multi-argument "emplace" initializer
1170 // The arguments are wrapped into tbb::internal::stored_pack to avoid variadic templates in ets_copy_assign_test.
1171 test_helper<T>::set(src_init,EXPECTED*17);
1172 ets_copy_assign_test< std::pair<int,T>, Allocator>(tbb::internal::save_pack(17,src_init), std::make_pair(-1,T()), Validator<T,EXPECTED>(), allocator_name);
1173#endif
1174 #undef EXPECTED
1175}
1176
1177template< template<class> class Allocator>
1178void run_assignment_and_copy_constructor_tests(const char* allocator_name) {
1179 REMARK("Running assignment and copy constructor tests\n");
1180 run_assign_and_copy_constructor_test<int, Allocator>("int", allocator_name);
1181 run_assign_and_copy_constructor_test<double, Allocator>("double", allocator_name);
1182 // Try class sizes that are close to a cache line in size, in order to check padding calculations.
1183 run_assign_and_copy_constructor_test<minimal<line_size-1>, Allocator >("minimal<line_size-1>", allocator_name);
1184 run_assign_and_copy_constructor_test<minimal<line_size>, Allocator >("minimal<line_size>", allocator_name);
1185 run_assign_and_copy_constructor_test<minimal<line_size+1>, Allocator >("minimal<line_size+1>", allocator_name);
1186 ASSERT(FinitCounter==0, NULL);
1187}
1188
1189// Class with no default constructor
1190class HasNoDefaultConstructor {
1191 HasNoDefaultConstructor();
1192public:
1193 HasNoDefaultConstructor( SecretTagType ) {}
1194};
1195// Initialization functor for HasNoDefaultConstructor
1196struct HasNoDefaultConstructorFinit {
1197 HasNoDefaultConstructor operator()() {
1198 return HasNoDefaultConstructor(SecretTag);
1199 }
1200};
1201// Combine functor for HasNoDefaultConstructor
1202struct HasNoDefaultConstructorCombine {
1203 HasNoDefaultConstructor operator()( HasNoDefaultConstructor, HasNoDefaultConstructor ) {
1204 return HasNoDefaultConstructor(SecretTag);
1205 }
1206};
1207
1208#if __TBB_ETS_USE_CPP11
1209// Class that only has a constructor with multiple parameters and a move constructor
1210class HasSpecialAndMoveCtor : NoCopy {
1211 HasSpecialAndMoveCtor();
1212public:
1213 HasSpecialAndMoveCtor( SecretTagType, size_t = size_t(0), const char* = "" ) {}
1214 HasSpecialAndMoveCtor( HasSpecialAndMoveCtor&& ) {}
1215};
1216#endif
1217
1218// No-op combine-each functor
1219template<typename V>
1220struct EmptyCombineEach {
1221 void operator()( const V& ) { }
1222};
1223
1224int
1225align_val(void * const p) {
1226 size_t tmp = (size_t)p;
1227 int a = 1;
1228 while((tmp&0x1) == 0) { a <<=1; tmp >>= 1; }
1229 return a;
1230}
1231
1232bool is_between(void* lowp, void *highp, void *testp) {
1233 if((size_t)lowp < (size_t)testp && (size_t)testp < (size_t)highp) return true;
1234 return (size_t)lowp > (size_t)testp && (size_t)testp > (size_t)highp;
1235}
1236
1237template<class U> struct alignment_of {
1238 typedef struct { char t; U padded; } test_alignment;
1239 static const size_t value = sizeof(test_alignment) - sizeof(U);
1240};
1241using tbb::interface6::internal::ets_element;
1242template<typename T, typename OtherType>
1243void allocate_ets_element_on_stack(const char *name) {
1244 typedef T aligning_element_type;
1245 const size_t my_align = alignment_of<aligning_element_type>::value;
1246 OtherType c1;
1247 ets_element<aligning_element_type> my_stack_element;
1248 OtherType c2;
1249 ets_element<aligning_element_type> my_stack_element2;
1250 struct {
1251 OtherType cxx;
1252 ets_element<aligning_element_type> my_struct_element;
1253 } mystruct1;
1254 tbb::internal::suppress_unused_warning(c1,c2);
1255 REMARK("using %s, c1 address == %lx (alignment %d), c2 address == %lx (alignment %d)\n", name, &c1, align_val(&c1), &c2, align_val(&c2));
1256 REMARK(" ---- my_align == %d\n", (int)my_align);
1257 REMARK(" my_stack_element == %lx (alignment %d), my_stack_element2 == %lx (alignment %d)\n",
1258 &my_stack_element, align_val(&my_stack_element), &my_stack_element2, align_val(&my_stack_element2));
1259 if(is_between(&c1,&c2,&my_stack_element)) REMARK("my_struct_element is in the middle\n");
1260 if(is_between(&c1,&c2,&my_stack_element2)) REMARK("my_struct_element2 is in the middle\n");
1261 if(!is_between(&c1,&c2,&my_stack_element) && !is_between(&c1,&c2,&my_stack_element2)) REMARK("stack vars reorganized\n");
1262 REMARK(" structure field address == %lx, alignment %d\n",
1263 mystruct1.my_struct_element.value(),
1264 align_val(mystruct1.my_struct_element.value())
1265 );
1266 ASSERT(tbb::internal::is_aligned(my_stack_element.value(), my_align), "Error in first stack alignment" );
1267 ASSERT(tbb::internal::is_aligned(my_stack_element2.value(), my_align), "Error in second stack alignment" );
1268 ASSERT(tbb::internal::is_aligned(mystruct1.my_struct_element.value(), my_align), "Error in struct element alignment" );
1269}
1270
1271//! Test situations where only default constructor or copy constructor is required.
1272template<template<class> class Allocator>
1273void TestInstantiation(const char *allocator_name) {
1274 REMARK("TestInstantiation<%s>\n", allocator_name);
1275 // Test instantiation is possible when copy constructor is not required.
1276 tbb::enumerable_thread_specific<NoCopy, Allocator<NoCopy> > ets1;
1277 ets1.local();
1278 ets1.combine_each(EmptyCombineEach<NoCopy>());
1279
1280 // Test instantiation when default constructor is not required, because exemplar is provided.
1281 HasNoDefaultConstructor x(SecretTag);
1282 tbb::enumerable_thread_specific<HasNoDefaultConstructor, Allocator<HasNoDefaultConstructor> > ets2(x);
1283 ets2.local();
1284 ets2.combine(HasNoDefaultConstructorCombine());
1285
1286 // Test instantiation when default constructor is not required, because init function is provided.
1287 HasNoDefaultConstructorFinit f;
1288 tbb::enumerable_thread_specific<HasNoDefaultConstructor, Allocator<HasNoDefaultConstructor> > ets3(f);
1289 ets3.local();
1290 ets3.combine(HasNoDefaultConstructorCombine());
1291
1292#if __TBB_ETS_USE_CPP11
1293 // Test instantiation with multiple arguments
1294 tbb::enumerable_thread_specific<HasSpecialAndMoveCtor, Allocator<HasSpecialAndMoveCtor> > ets4(SecretTag, 0x42, "meaningless");
1295 ets4.local();
1296 ets4.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
1297 // Test instantiation with one argument that should however use the variadic constructor
1298 tbb::enumerable_thread_specific<HasSpecialAndMoveCtor, Allocator<HasSpecialAndMoveCtor> > ets5(SecretTag);
1299 ets5.local();
1300 ets5.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
1301 // Test that move operations do not impose extra requirements
1302 // Default allocator is used. If it does not match Allocator, there will be elementwise move
1303 tbb::enumerable_thread_specific<HasSpecialAndMoveCtor> ets6( std::move(ets4) );
1304 ets6.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
1305 ets6 = std::move(ets5);
1306#endif
1307}
1308
1309class BigType {
1310public:
1311 BigType() { /* avoid cl warning C4345 about default initialization of POD types */ }
1312 char my_data[12 * 1024 * 1024];
1313};
1314
1315template<template<class> class Allocator>
1316void TestConstructorWithBigType(const char *allocator_name) {
1317 typedef tbb::enumerable_thread_specific<BigType, Allocator<BigType> > CounterBigType;
1318 REMARK("TestConstructorWithBigType<%s>\n", allocator_name);
1319 // Test default constructor
1320 CounterBigType MyCounters;
1321 // Create a local instance.
1322 typename CounterBigType::reference my_local = MyCounters.local();
1323 my_local.my_data[0] = 'a';
1324 // Test copy constructor
1325 CounterBigType MyCounters2(MyCounters);
1326 ASSERT(check_alignment(MyCounters2.local(), allocator_name).my_data[0]=='a', NULL);
1327}
1328
1329int TestMain () {
1330 size_t tbb_allocator_mask;
1331 size_t cache_allocator_mask = tbb::internal::NFS_GetLineSize();
1332 REMARK("estimatedCacheLineSize == %d, NFS_GetLineSize() returns %d\n",
1333 (int)estimatedCacheLineSize, (int)tbb::internal::NFS_GetLineSize());
1334 //TODO: use __TBB_alignof(T) to check for local() results instead of using internal knowledges of ets element padding
1335 if(tbb::tbb_allocator<int>::allocator_type() == tbb::tbb_allocator<int>::standard) {
1336 // scalable allocator is not available.
1337 tbb_allocator_mask = 1;
1338 REMARK("tbb::tbb_allocator is not available\n");
1339 }
1340 else {
1341 // this value is for large objects, but will be correct for small.
1342 tbb_allocator_mask = estimatedCacheLineSize;
1343 }
1344 AlignMask = cache_allocator_mask;
1345 TestInstantiation<tbb::cache_aligned_allocator>("tbb::cache_aligned_allocator");
1346 AlignMask = tbb_allocator_mask;
1347 TestInstantiation<tbb::tbb_allocator>("tbb::tbb_allocator");
1348 AlignMask = cache_allocator_mask;
1349 run_assignment_and_copy_constructor_tests<tbb::cache_aligned_allocator>("tbb::cache_aligned_allocator");
1350 AlignMask = tbb_allocator_mask;
1351 run_assignment_and_copy_constructor_tests<tbb::tbb_allocator>("tbb::tbb_allocator");
1352 run_segmented_iterator_tests();
1353 flog_key_creation_and_deletion();
1354
1355 if (MinThread == 0) {
1356 run_serial_tests();
1357 MinThread = 1;
1358 }
1359 if (MaxThread > 0) {
1360 AlignMask = cache_allocator_mask;
1361 run_parallel_tests<tbb::cache_aligned_allocator>("tbb::cache_aligned_allocator");
1362 AlignMask = tbb_allocator_mask;
1363 run_parallel_tests<tbb::tbb_allocator>("tbb::tbb_allocator");
1364 run_cross_type_tests();
1365 }
1366
1367 AlignMask = cache_allocator_mask;
1368 TestConstructorWithBigType<tbb::cache_aligned_allocator>("tbb::cache_aligned_allocator");
1369 AlignMask = tbb_allocator_mask;
1370 TestConstructorWithBigType<tbb::tbb_allocator>("tbb::tbb_allocator");
1371
1372 allocate_ets_element_on_stack<int,char>("int vs. char");
1373 allocate_ets_element_on_stack<int,short>("int vs. short");
1374 allocate_ets_element_on_stack<int,char[3]>("int vs. char[3]");
1375 allocate_ets_element_on_stack<float,char>("float vs. char");
1376 allocate_ets_element_on_stack<float,short>("float vs. short");
1377 allocate_ets_element_on_stack<float,char[3]>("float vs. char[3]");
1378
1379 return Harness::Done;
1380}
1381