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/utils_report.h"
20 #include "common/checktype.h"
21 
22 #include "oneapi/tbb/detail/_utils.h"
23 
24 #include "tbb/enumerable_thread_specific.h"
25 #include "tbb/parallel_for.h"
26 #include "tbb/blocked_range.h"
27 #include "tbb/tbb_allocator.h"
28 #include "tbb/global_control.h"
29 #include "tbb/cache_aligned_allocator.h"
30 
31 #include <cstring>
32 #include <cstdio>
33 #include <vector>
34 #include <deque>
35 #include <list>
36 #include <map>
37 #include <utility>
38 #include <atomic>
39 
40 //! \file test_enumerable_thread_specific.cpp
41 //! \brief Test for [tls.enumerable_thread_specific] specification
42 
43 //! Minimum number of threads
44 static int MinThread = 1;
45 
46 //! Maximum number of threads
47 static int MaxThread = 4;
48 
49 static std::atomic<int> construction_counter;
50 static std::atomic<int> destruction_counter;
51 
52 const int VALID_NUMBER_OF_KEYS = 100;
53 
54 //! A minimal class that occupies N bytes.
55 /** Defines default and copy constructor, and allows implicit operator&. Hides operator=. */
56 template<size_t N = tbb::detail::max_nfs_size>
57 class minimal: utils::NoAssign {
58 private:
59     int my_value;
60     bool is_constructed;
61     char pad[N-sizeof(int) - sizeof(bool)];
62 public:
63     minimal() : utils::NoAssign(), my_value(0) { ++construction_counter; is_constructed = true; }
64     minimal( const minimal &m ) : utils::NoAssign(), my_value(m.my_value) { ++construction_counter; is_constructed = true; }
65     ~minimal() { ++destruction_counter; REQUIRE(is_constructed); is_constructed = false; }
66     void set_value( const int i ) { REQUIRE(is_constructed); my_value = i; }
67     int value( ) const { REQUIRE(is_constructed); return my_value; }
68 };
69 
70 static size_t AlignMask = 0;  // set to cache-line-size - 1
71 
72 template<typename T>
73 T& check_alignment(T& t, const char *aname) {
74     if( !tbb::detail::is_aligned(&t, AlignMask)) {
75         // TBB_REVAMP_TODO: previously was REPORT_ONCE
76         REPORT("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
77     }
78     return t;
79 }
80 
81 template<typename T>
82 const T& check_alignment(const T& t, const char *aname) {
83     if( !tbb::detail::is_aligned(&t, AlignMask)) {
84         // TBB_REVAMP_TODO: previously was REPORT_ONCE
85         REPORT("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
86     }
87     return t;
88 }
89 
90 //
91 // A helper class that simplifies writing the tests since minimal does not
92 // define = or + operators.
93 //
94 
95 const size_t line_size = tbb::detail::max_nfs_size;
96 
97 typedef tbb::enumerable_thread_specific<minimal<line_size> > flogged_ets;
98 
99 class set_body {
100     flogged_ets *a;
101 
102 public:
103     set_body( flogged_ets*_a ) : a(_a) { }
104 
105     void operator() ( ) const {
106         for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
107             check_alignment(a[i].local(), "default").set_value(i + 1);
108         }
109     }
110 
111 };
112 
113 void do_std_threads( int max_threads, flogged_ets a[] ) {
114     std::vector< std::thread * > threads;
115 
116     for (int p = 0; p < max_threads; ++p) {
117         threads.push_back( new std::thread ( set_body( a ) ) );
118     }
119 
120     for (int p = 0; p < max_threads; ++p) {
121         threads[p]->join();
122     }
123 
124     for(int p = 0; p < max_threads; ++p) {
125         delete threads[p];
126     }
127 }
128 
129 void flog_key_creation_and_deletion() {
130     const int FLOG_REPETITIONS = 100;
131 
132     for (int p = MinThread; p <= MaxThread; ++p) {
133         for (int j = 0; j < FLOG_REPETITIONS; ++j) {
134             construction_counter = 0;
135             destruction_counter = 0;
136             // causes VALID_NUMBER_OF_KEYS exemplar instances to be constructed
137             flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS];
138             REQUIRE(int(construction_counter) == 0);   // no exemplars or actual locals have been constructed
139             REQUIRE(int(destruction_counter) == 0);    // and none have been destroyed
140             // causes p * VALID_NUMBER_OF_KEYS minimals to be created
141             do_std_threads(p, a);
142             for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
143                 int pcnt = 0;
144                 for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) {
145                     REQUIRE( (*tli).value() == i+1 );
146                     ++pcnt;
147                 }
148                 REQUIRE( pcnt == p);  // should be one local per thread.
149             }
150             delete[] a;
151         }
152         REQUIRE( int(construction_counter) == (p)*VALID_NUMBER_OF_KEYS );
153         REQUIRE( int(destruction_counter) == (p)*VALID_NUMBER_OF_KEYS );
154 
155         construction_counter = 0;
156         destruction_counter = 0;
157 
158         // causes VALID_NUMBER_OF_KEYS exemplar instances to be constructed
159         flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS];
160 
161         for (int j = 0; j < FLOG_REPETITIONS; ++j) {
162             // causes p * VALID_NUMBER_OF_KEYS minimals to be created
163             do_std_threads(p, a);
164 
165             for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
166                 for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) {
167                     REQUIRE( (*tli).value() == i+1 );
168                 }
169                 a[i].clear();
170                 REQUIRE( static_cast<int>(a[i].end() - a[i].begin()) == 0 );
171             }
172         }
173         delete[] a;
174         REQUIRE( int(construction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS );
175         REQUIRE( int(destruction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS );
176     }
177 
178 }
179 
180 template <typename inner_container>
181 void flog_segmented_interator() {
182 
183     bool found_error = false;
184     typedef typename inner_container::value_type T;
185     typedef std::vector< inner_container > nested_vec;
186     inner_container my_inner_container;
187     my_inner_container.clear();
188     nested_vec my_vec;
189 
190     // simple nested vector (neither level empty)
191     const int maxval = 10;
192     for(int i=0; i < maxval; i++) {
193         my_vec.push_back(my_inner_container);
194         for(int j = 0; j < maxval; j++) {
195             my_vec.at(i).push_back((T)(maxval * i + j));
196         }
197     }
198 
199     tbb::detail::d1::segmented_iterator<nested_vec, T> my_si(my_vec);
200 
201     T ii;
202     for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
203         if((*my_si) != ii) {
204             found_error = true;
205         }
206     }
207 
208     // outer level empty
209     my_vec.clear();
210     for(my_si=my_vec.begin(); my_si != my_vec.end(); ++my_si) {
211         found_error = true;
212     }
213 
214     // inner levels empty
215     my_vec.clear();
216     for(int i =0; i < maxval; ++i) {
217         my_vec.push_back(my_inner_container);
218     }
219     for(my_si = my_vec.begin(); my_si != my_vec.end(); ++my_si) {
220         found_error = true;
221     }
222 
223     // every other inner container is empty
224     my_vec.clear();
225     for(int i=0; i < maxval; ++i) {
226         my_vec.push_back(my_inner_container);
227         if(i%2) {
228             for(int j = 0; j < maxval; ++j) {
229                 my_vec.at(i).push_back((T)(maxval * (i/2) + j));
230             }
231         }
232     }
233     for(my_si = my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
234         if((*my_si) != ii) {
235             found_error = true;
236         }
237     }
238 
239     tbb::detail::d1::segmented_iterator<nested_vec, const T> my_csi(my_vec);
240     for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
241         if((*my_csi) != ii) {
242             found_error = true;
243         }
244     }
245 
246     // outer level empty
247     my_vec.clear();
248     for(my_csi=my_vec.begin(); my_csi != my_vec.end(); ++my_csi) {
249         found_error = true;
250     }
251 
252     // inner levels empty
253     my_vec.clear();
254     for(int i =0; i < maxval; ++i) {
255         my_vec.push_back(my_inner_container);
256     }
257     for(my_csi = my_vec.begin(); my_csi != my_vec.end(); ++my_csi) {
258         found_error = true;
259     }
260 
261     // every other inner container is empty
262     my_vec.clear();
263     for(int i=0; i < maxval; ++i) {
264         my_vec.push_back(my_inner_container);
265         if(i%2) {
266             for(int j = 0; j < maxval; ++j) {
267                 my_vec.at(i).push_back((T)(maxval * (i/2) + j));
268             }
269         }
270     }
271     for(my_csi = my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
272         if((*my_csi) != ii) {
273             found_error = true;
274         }
275     }
276 
277 
278     if(found_error) REPORT("segmented_iterator failed\n");
279 }
280 
281 template <typename Key, typename Val>
282 void flog_segmented_iterator_map() {
283    typedef typename std::map<Key, Val> my_map;
284    typedef std::vector< my_map > nested_vec;
285    my_map my_inner_container;
286    my_inner_container.clear();
287    nested_vec my_vec;
288    my_vec.clear();
289    bool found_error = false;
290 
291    // simple nested vector (neither level empty)
292    const int maxval = 4;
293    for(int i=0; i < maxval; i++) {
294        my_vec.push_back(my_inner_container);
295        for(int j = 0; j < maxval; j++) {
296            my_vec.at(i).insert(std::make_pair<Key,Val>(maxval * i + j, 2*(maxval*i + j)));
297        }
298    }
299 
300    tbb::detail::d1::segmented_iterator<nested_vec, std::pair<const Key, Val> > my_si(my_vec);
301    Key ii;
302    for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
303        if(((*my_si).first != ii) || ((*my_si).second != 2*ii)) {
304            found_error = true;
305        }
306    }
307 
308    tbb::detail::d1::segmented_iterator<nested_vec, const std::pair<const Key, Val> > my_csi(my_vec);
309    for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
310        if(((*my_csi).first != ii) || ((*my_csi).second != 2*ii)) {
311            found_error = true;
312            // INFO( "ii=%d, (*my_csi).first=%d, second=%d\n",ii, int((*my_csi).first), int((*my_csi).second));
313        }
314    }
315    if(found_error) REPORT("segmented_iterator_map failed\n");
316 }
317 
318 void run_segmented_iterator_tests() {
319    // only the following containers can be used with the segmented iterator.
320    flog_segmented_interator<std::vector< int > >();
321    flog_segmented_interator<std::vector< double > >();
322    flog_segmented_interator<std::deque< int > >();
323    flog_segmented_interator<std::deque< double > >();
324    flog_segmented_interator<std::list< int > >();
325    flog_segmented_interator<std::list< double > >();
326 
327    flog_segmented_iterator_map<int, int>();
328    flog_segmented_iterator_map<int, double>();
329 }
330 
331 int align_val(void * const p) {
332     size_t tmp = (size_t)p;
333     int a = 1;
334     while((tmp&0x1) == 0) { a <<=1; tmp >>= 1; }
335     return a;
336 }
337 
338 bool is_between(void* lowp, void *highp, void *testp) {
339     if((size_t)lowp < (size_t)testp && (size_t)testp < (size_t)highp) return true;
340     return (size_t)lowp > (size_t)testp && (size_t)testp > (size_t)highp;
341 }
342 
343 template<class U> struct alignment_of {
344     typedef struct { char t; U    padded; } test_alignment;
345     static const size_t value = sizeof(test_alignment) - sizeof(U);
346 };
347 using tbb::detail::d1::ets_element;
348 template<typename T, typename OtherType>
349 void allocate_ets_element_on_stack(const char* /* name */) {
350     typedef T aligning_element_type;
351     const size_t my_align = alignment_of<aligning_element_type>::value;
352     OtherType c1;
353     ets_element<aligning_element_type> my_stack_element;
354     OtherType c2;
355     ets_element<aligning_element_type> my_stack_element2;
356     struct {
357         OtherType cxx;
358         ets_element<aligning_element_type> my_struct_element;
359     } mystruct1;
360     tbb::detail::suppress_unused_warning(c1,c2);
361     REQUIRE_MESSAGE(tbb::detail::is_aligned(my_stack_element.value(), my_align), "Error in first stack alignment" );
362     REQUIRE_MESSAGE(tbb::detail::is_aligned(my_stack_element2.value(), my_align), "Error in second stack alignment" );
363     REQUIRE_MESSAGE(tbb::detail::is_aligned(mystruct1.my_struct_element.value(), my_align), "Error in struct element alignment" );
364 }
365 
366 class BigType {
367 public:
368     BigType() { /* avoid cl warning C4345 about default initialization of POD types */ }
369     char my_data[12 * 1024 * 1024];
370 };
371 
372 template<template<class> class Allocator>
373 void TestConstructorWithBigType(const char* allocator_name) {
374     typedef tbb::enumerable_thread_specific<BigType, Allocator<BigType> > CounterBigType;
375     // Test default constructor
376     CounterBigType MyCounters;
377     // Create a local instance.
378     typename CounterBigType::reference my_local = MyCounters.local();
379     my_local.my_data[0] = 'a';
380     // Test copy constructor
381     CounterBigType MyCounters2(MyCounters);
382     REQUIRE(check_alignment(MyCounters2.local(), allocator_name).my_data[0]=='a');
383 }
384 
385 size_t init_tbb_alloc_mask() {
386     // TODO: use __TBB_alignof(T) to check for local() results instead of using internal knowledges of ets element padding
387     if(tbb::tbb_allocator<int>::allocator_type() == tbb::tbb_allocator<int>::standard) {
388         // scalable allocator is not available.
389         // INFO("tbb::tbb_allocator is not available\n");
390         return 1;
391     }
392     else {
393         // this value is for large objects, but will be correct for small.
394         return 64; // TBB_REVAMP_TODO: enable as estimatedCacheLineSize when tbbmalloc is available;
395     }
396 }
397 
398 static const size_t cache_allocator_mask = tbb::detail::r1::cache_line_size();
399 static const size_t tbb_allocator_mask = init_tbb_alloc_mask();
400 
401 //! Test for internal segmented_iterator type, used inside flattened2d class
402 //! \brief \ref error_guessing
403 TEST_CASE("Segmented iterator") {
404     AlignMask = tbb_allocator_mask;
405     run_segmented_iterator_tests();
406 }
407 
408 //! Test ETS keys creation/deletion
409 //! \brief \ref error_guessing \ref boundary
410 TEST_CASE("Key creation and deletion") {
411     AlignMask = tbb_allocator_mask;
412     flog_key_creation_and_deletion();
413 }
414 
415 //! Test construction with big ETS types
416 //! \brief \ref error_guessing
417 TEST_CASE("Constructor with big type") {
418     AlignMask = cache_allocator_mask;
419     TestConstructorWithBigType<tbb::cache_aligned_allocator>("tbb::cache_aligned_allocator");
420     AlignMask = tbb_allocator_mask;
421     TestConstructorWithBigType<tbb::tbb_allocator>("tbb::tbb_allocator");
422 }
423 
424 //! Test allocation of ETS elements on the stack (internal types)
425 //! \brief \ref error_guessing
426 TEST_CASE("Allocate ETS on stack") {
427     AlignMask = tbb_allocator_mask;
428     allocate_ets_element_on_stack<int,char>("int vs. char");
429     allocate_ets_element_on_stack<int,short>("int vs. short");
430     allocate_ets_element_on_stack<int,char[3]>("int vs. char[3]");
431     allocate_ets_element_on_stack<float,char>("float vs. char");
432     allocate_ets_element_on_stack<float,short>("float vs. short");
433     allocate_ets_element_on_stack<float,char[3]>("float vs. char[3]");
434 }
435 
436