1 /*
2     Copyright (c) 2019-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 "test_concurrent_associative_common.h"
18 
19 // Now empty ordered container allocations count is checked by upper bound (calculated manually)
20 const size_t dummy_head_max_size = 584;
21 
22 template<typename MyTable>
CheckEmptyContainerAllocator(MyTable & table,size_t expected_allocs,size_t expected_frees,bool exact,int line)23 inline void CheckEmptyContainerAllocator(MyTable &table, size_t expected_allocs, size_t expected_frees, bool exact, int line) {
24     typename MyTable::allocator_type a = table.get_allocator();
25     REMARK("#%d checking allocators: items %u/%u, allocs %u/%u\n", line,
26         unsigned(a.items_allocated), unsigned(a.items_freed), unsigned(a.allocations), unsigned(a.frees) );
27     CheckAllocator<MyTable>(a, expected_allocs, expected_frees, exact);
28     ASSERT( a.items_allocated <= a.items_freed + dummy_head_max_size, NULL);
29 }
30 
31 template <typename Table>
32 struct order_checker {
33     typename Table::value_compare& val_comp;
34     typename Table::key_compare& key_comp;
35 
order_checkerorder_checker36     order_checker(typename Table::value_compare& _val_c,typename Table::key_compare& _key_c): val_comp(_val_c), key_comp(_key_c){}
37 
38 
operatororder_checker39     bool operator()(const typename Table::value_type& lhs, const typename Table::value_type& rhs){
40         if (Table::allow_multimapping)
41             // We need to use not greater comparator for multicontainers
42             return !val_comp(rhs, lhs) && !key_comp(Value<Table>::key(rhs), Value<Table>::key(lhs));
43         return val_comp(lhs,rhs) && key_comp(Value<Table>::key(lhs),Value<Table>::key(rhs));
44     }
45 };
46 
47 template< typename Table>
check_container_order(const Table & cont)48 void check_container_order(const Table& cont) {
49     if (!cont.empty()){
50         typename Table::key_compare key_comp = cont.key_comp();
51         typename Table::value_compare value_comp = cont.value_comp();
52         order_checker<Table> check_order(value_comp, key_comp);
53 
54         for (auto it = cont.begin(); std::next(it)!=cont.end();){
55             auto pr_it = it++;
56             ASSERT(check_order(*pr_it, *it),"The order of the elements is broken");
57         }
58     }
59 }
60 
61 template <typename T>
test_ordered_methods()62 void test_ordered_methods() {
63     T cont;
64 
65     int r, random_threshold = 10, uncontained_key = random_threshold / 2;
66     for (int i = 0; i < 100; i++) {
67         r = std::rand() % random_threshold;
68         if ( r != uncontained_key) {
69             cont.insert(Value<T>::make(r));
70         }
71     }
72 
73     check_container_order(cont);
74 
75     typename T::value_compare val_comp = cont.value_comp();
76     typename T::iterator l_bound_check, u_bound_check;
77     for (int key = -1; key < random_threshold + 1; key++) {
78 
79         auto eq_range = cont.equal_range(key);
80         // Check equal_range() content
81         for (auto it = eq_range.first; it != eq_range.second; it++)
82             ASSERT(*it == Value<T>::make(key), "equal_range() contain wrong value");
83 
84         // Manual search of upper and lower bounds
85         l_bound_check = cont.end();
86         u_bound_check = cont.end();
87         for (auto it = cont.begin() ; it != cont.end(); it++){
88             if (!val_comp(*it, Value<T>::make(key)) && l_bound_check == cont.end()){
89                 l_bound_check = it;
90             }
91             if (val_comp(Value<T>::make(key),*it) && u_bound_check == cont.end()){
92                 u_bound_check = it;
93                 break;
94             }
95         }
96 
97         typename T::iterator l_bound = cont.lower_bound(key);
98         typename T::iterator u_bound = cont.upper_bound(key);
99 
100         ASSERT(l_bound == l_bound_check, "lower_bound() contains wrong value");
101         ASSERT(u_bound == u_bound_check, "upper_bound() contains wrong value");
102 
103         ASSERT(l_bound == eq_range.first && u_bound == eq_range.second, NULL);
104     }
105 }
106 
107 template<typename T, typename do_check_element_state>
test_basic(const char * str,do_check_element_state)108 void test_basic(const char * str, do_check_element_state)
109 {
110     test_basic_common<T>(str, do_check_element_state());
111     test_ordered_methods<T>();
112 }
113 
114 template<typename T>
test_basic(const char * str)115 void test_basic(const char * str){
116     test_basic_common<T>(str);
117     test_ordered_methods<T>();
118 }
119 
120 template<typename T>
test_concurrent_order()121 void test_concurrent_order() {
122     for (auto num_threads = MinThread + 1; num_threads <= MaxThread; num_threads++) {
123         T cont;
124         int items = 1000;
125         NativeParallelFor( num_threads, [&](size_t index){
126             int step = index % 4 + 1;
127             bool reverse = (step % 2 == 0);
128             if (reverse) {
129                 for (int i = 0; i < items; i+=step){
130                     cont.insert(Value<T>::make(i));
131                 }
132             } else {
133                 for (int i = items; i > 0; i-=step){
134                     cont.insert(Value<T>::make(i));
135                 }
136             }
137         } );
138 
139         check_container_order(cont);
140     }
141 }
142 
143 template<typename T>
144 void test_concurrent(const char *tablename, bool asymptotic = false) {
145     test_concurrent_common<T>(tablename, asymptotic);
146     test_concurrent_order<T>();
147 }
148 
149 // If the inserted elements look the same for the comparator,
150 // they must be inserted in order from the first inserted to the last.
151 template<typename T>
check_multicontainer_internal_order()152 void check_multicontainer_internal_order(){
153     T cont;
154     for (int counter = 0; counter < 10; counter++){
155         cont.emplace(1, counter);
156     }
157 
158     for ( auto it = cont.begin(); std::next(it) != cont.end();){
159         auto it_pr = it++;
160         ASSERT(it_pr->second < it->second, "Internal multicontainers order is broken");
161     }
162 }
163 
164 struct ordered_move_traits_base {
165     enum{ expected_number_of_items_to_allocate_for_steal_move = dummy_head_max_size };
166 
167     template <typename ordered_type, typename iterator_type>
construct_containerordered_move_traits_base168     static ordered_type& construct_container(tbb::aligned_space<ordered_type> & storage, iterator_type begin, iterator_type end){
169         new (storage.begin()) ordered_type(begin, end);
170         return * storage.begin();
171     }
172 
173     template <typename ordered_type, typename iterator_type, typename allocator_type>
construct_containerordered_move_traits_base174     static ordered_type& construct_container(tbb::aligned_space<ordered_type> & storage, iterator_type begin, iterator_type end, allocator_type const& a ){
175         new (storage.begin()) ordered_type(begin, end, typename ordered_type::key_compare(), a);
176         return * storage.begin();
177     }
178 
179     template<typename ordered_type, typename iterator>
equalordered_move_traits_base180     static bool equal(ordered_type const& c, iterator begin, iterator end){
181         bool equal_sizes = ( static_cast<size_t>(std::distance(begin, end)) == c.size() );
182         if (!equal_sizes)
183             return false;
184         for (iterator it = begin; it != end; ++it ){
185             if (c.find( Value<ordered_type>::key(*it)) == c.end()){
186                 return false;
187             }
188         }
189         return true;
190     }
191 };
192 
193 namespace std {
194     template<> struct less< std::weak_ptr<int> > {
195     public:
196         size_t operator()( const std::weak_ptr<int>& lhs, const std::weak_ptr<int>& rhs ) const { return *lhs.lock() < * rhs.lock(); }
197     };
198     template<> struct less< const std::weak_ptr<int> > {
199     public:
200         size_t operator()( const std::weak_ptr<int>& lhs, const std::weak_ptr<int>& rhs ) const { return *lhs.lock() < * rhs.lock(); }
201     };
202 }
203 
204 template <bool defCtorPresent, typename Table>
205 void CustomExamine( Table, const std::list<typename Table::value_type>) {
206     /*order check - see unordered example*/
207 }
208 
209 template <bool defCtorPresent, typename Table>
210 void Examine( Table c, const std::list<typename Table::value_type> &lst) {
211     CommonExamine<defCtorPresent>(c, lst);
212     CustomExamine<defCtorPresent>(c, lst);
213 }
214 
215 template <bool defCtorPresent, typename Table, typename TableDebugAlloc>
216 void TypeTester( const std::list<typename Table::value_type> &lst ) {
217     ASSERT( lst.size() >= 5, "Array should have at least 5 elements" );
218     ASSERT( lst.size() <= 100, "The test has O(n^2) complexity so a big number of elements can lead long execution time" );
219     // Construct an empty table.
220     Table c1;
221     c1.insert( lst.begin(), lst.end() );
222     Examine<defCtorPresent>( c1, lst );
223 
224     typename Table::key_compare compare;
225 
226     typename Table::allocator_type allocator;
227 #if __TBB_INITIALIZER_LISTS_PRESENT && !__TBB_CPP11_INIT_LIST_TEMP_OBJS_LIFETIME_BROKEN
228     // Constructor from an initializer_list.
229     typename std::list<typename Table::value_type>::const_iterator it = lst.begin();
230     Table c2( { *it++, *it++, *it++ } );
231     c2.insert( it, lst.end( ) );
232     Examine<defCtorPresent>( c2, lst );
233 
234     it = lst.begin();
235     // Constructor from an initializer_list, default comparator and non-default allocator
236     Table c2_alloc( { *it++, *it++, *it++ }, allocator);
237     c2_alloc.insert( it, lst.end() );
238     Examine<defCtorPresent>( c2_alloc, lst );
239 
240     it = lst.begin();
241     // Constructor from an initializer_list, non-default comparator and allocator
242     Table c2_comp_alloc( { *it++, *it++, *it++ }, compare, allocator );
243     c2_comp_alloc.insert( it, lst.end() );
244     Examine<defCtorPresent>( c2_comp_alloc, lst );
245 #endif
246     // Copying constructor.
247     Table c3( c1 );
248     Examine<defCtorPresent>( c3, lst );
249     // Construct with non-default allocator
250     TableDebugAlloc c4;
251     c4.insert( lst.begin(), lst.end() );
252     Examine<defCtorPresent>( c4, lst );
253     // Copying constructor for a container with a different allocator type.
254     TableDebugAlloc c5( c4 );
255     Examine<defCtorPresent>( c5, lst );
256 
257     // Construction empty table with non-default comparator
258     Table c6( compare );
259     c6.insert( lst.begin(), lst.end() );
260     Examine<defCtorPresent>( c6, lst );
261 
262     // Construction empty table with non-default allocator
263     Table c6_alloc( allocator );
264     c6_alloc.insert( lst.begin(), lst.end() );
265     Examine<defCtorPresent>( c6_alloc, lst );
266 
267     // Construction empty table with a non-default comparator and allocator
268     Table c6_comp_alloc( compare, allocator );
269     c6_comp_alloc.insert( lst.begin(), lst.end() );
270     Examine<defCtorPresent>( c6_alloc, lst );
271 
272     // Construction empty table with a  non-default comparator and allocator
273     TableDebugAlloc c7( compare );
274     c7.insert( lst.begin(), lst.end() );
275     Examine<defCtorPresent>( c7, lst );
276 
277     // Construction with a copying iteration range and a given allocator instance.
278     Table c8( c1.begin(), c1.end() );
279     Examine<defCtorPresent>( c8, lst );
280 
281     // Construction with a copying iteration range, default compare and non-default allocator
282     Table c8_alloc( c1.begin(), c1.end(), allocator );
283     Examine<defCtorPresent>( c8_alloc, lst );
284 
285     // Construction with a copying iteration range, non-default compare and allocator
286     Table c8_comp_alloc( c1.begin(), c1.end(), compare, allocator );
287     Examine<defCtorPresent>( c8_comp_alloc, lst);
288 
289     // Construction with an instance of non-default allocator
290     typename TableDebugAlloc::allocator_type a;
291     TableDebugAlloc c9( a );
292     c9.insert( c7.begin(), c7.end() );
293     Examine<defCtorPresent>( c9, lst );
294 }
295 
296 struct int_key {
297         int_key(int i) : my_item(i) {}
298         int my_item;
299 };
300 
301 bool operator<(const int_key& ik, int i) { return ik.my_item < i; }
302 bool operator<(int i, const int_key& ik) { return i < ik.my_item; }
303 bool operator<(const int_key& ik1, const int_key& ik2) { return ik1.my_item < ik2.my_item; }
304 
305 struct transparent_less {
306     template <typename T, typename U>
307     auto operator()( T&& lhs, U&& rhs ) const
308     -> decltype(std::forward<T>(lhs) < std::forward<U>(rhs)){
309         return lhs < rhs;
310     }
311 
312     using is_transparent = void;
313 };
314 
315 template <typename Container>
316 void check_heterogeneous_functions() {
317     static_assert(std::is_same<typename Container::key_type, int>::value,
318                   "incorrect key_type for heterogeneous lookup test");
319     // Initialization
320     Container c;
321     int size = 10;
322     for (int i = 0; i < size; i++){
323         c.insert(Value<Container>::make(i));
324     }
325     // Insert first duplicated element for multicontainers
326     if (Container::allow_multimapping){
327         c.insert(Value<Container>::make(0));
328     }
329 
330     // Look up testing
331     for (int i = 0; i < size; i++) {
332         int_key k(i);
333         int key = i;
334         ASSERT(c.find(k) == c.find(key), "Incorrect heterogeneous find return value");
335         ASSERT(c.lower_bound(k) == c.lower_bound(key), "Incorrect heterogeneous lower_bound return value");
336         ASSERT(c.upper_bound(k) == c.upper_bound(key), "Incorrect heterogeneous upper_bound return value");
337         ASSERT(c.equal_range(k) == c.equal_range(key), "Incorrect heterogeneous equal_range return value");
338         ASSERT(c.count(k) == c.count(key), "Incorrect heterogeneous count return value");
339         ASSERT(c.contains(k) == c.contains(key), "Incorrect heterogeneous contains return value");
340     }
341 
342     // Erase testing
343     for (int i = 0; i < size; i++){
344         auto count_before_erase = c.count(i);
345         auto result = c.unsafe_erase(int_key(i));
346         ASSERT(count_before_erase==result,"Incorrent erased elements count");
347         ASSERT(c.count(i)==0, "Some elements was not erased");
348     }
349 }
350 
351 template <template<typename...> class ContainerType, typename... ContainerArgs>
352 void test_allocator_traits() {
353     using namespace propagating_allocators;
354     using always_propagating_container = ContainerType<ContainerArgs..., always_propagating_allocator>;
355     using never_propagating_container = ContainerType<ContainerArgs..., never_propagating_allocator>;
356     using pocma_container = ContainerType<ContainerArgs..., pocma_allocator>;
357     using pocca_container = ContainerType<ContainerArgs..., pocca_allocator>;
358     using pocs_container = ContainerType<ContainerArgs..., pocs_allocator>;
359 
360     test_allocator_traits_support<always_propagating_container>();
361     test_allocator_traits_support<never_propagating_container>();
362     test_allocator_traits_support<pocma_container>();
363     test_allocator_traits_support<pocca_container>();
364     test_allocator_traits_support<pocs_container>();
365 
366     test_allocator_traits_with_non_movable_value_type<pocma_container>();
367 }
368 
369 // Comparator for scoped_allocator tests
370 struct allocator_data_compare {
371     template <typename A>
372     bool operator()(const allocator_aware_data<A>& d1, const allocator_aware_data<A>& d2) const {
373         return d1.value() < d2.value();
374     }
375 };
376