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