1 | /* |
2 | Copyright (c) 2005-2019 Intel Corporation |
3 | |
4 | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | you may not use this file except in compliance with the License. |
6 | You may obtain a copy of the License at |
7 | |
8 | http://www.apache.org/licenses/LICENSE-2.0 |
9 | |
10 | Unless required by applicable law or agreed to in writing, software |
11 | distributed under the License is distributed on an "AS IS" BASIS, |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | See the License for the specific language governing permissions and |
14 | limitations under the License. |
15 | */ |
16 | |
17 | #if _MSC_VER |
18 | #define _SCL_SECURE_NO_WARNINGS |
19 | #endif |
20 | |
21 | #include "tbb/concurrent_vector.h" |
22 | #include "tbb/tbb_allocator.h" |
23 | #include "tbb/cache_aligned_allocator.h" |
24 | #include "tbb/tbb_exception.h" |
25 | #include <cstdio> |
26 | #include <cstdlib> |
27 | #include <functional> |
28 | #include <vector> |
29 | #include <numeric> |
30 | #include "harness_report.h" |
31 | #include "harness_assert.h" |
32 | #include "harness_allocator.h" |
33 | #include "harness_defs.h" |
34 | #include "test_container_move_support.h" |
35 | |
36 | #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) |
37 | // Workaround for overzealous compiler warnings in /Wp64 mode |
38 | #pragma warning (push) |
39 | #pragma warning (disable: 4800) |
40 | #endif |
41 | |
42 | #if TBB_USE_EXCEPTIONS |
43 | static bool known_issue_verbose = false; |
44 | #define KNOWN_ISSUE(msg) if(!known_issue_verbose) known_issue_verbose = true, REPORT(msg) |
45 | #endif /* TBB_USE_EXCEPTIONS */ |
46 | |
47 | inline void NextSize( int& s ) { |
48 | if( s<=32 ) ++s; |
49 | else s += s/10; |
50 | } |
51 | |
52 | //! Check vector have expected size and filling |
53 | template<typename vector_t> |
54 | static void CheckVector( const vector_t& cv, size_t expected_size, size_t old_size ) { |
55 | ASSERT( cv.capacity()>=expected_size, NULL ); |
56 | ASSERT( cv.size()==expected_size, NULL ); |
57 | ASSERT( cv.empty()==(expected_size==0), NULL ); |
58 | for( int j=0; j<int(expected_size); ++j ) { |
59 | if( cv[j].bar()!=~j ) |
60 | REPORT("ERROR on line %d for old_size=%ld expected_size=%ld j=%d\n" ,__LINE__,long(old_size),long(expected_size),j); |
61 | } |
62 | } |
63 | |
64 | //! Test of assign, grow, copying with various sizes |
65 | void TestResizeAndCopy() { |
66 | typedef static_counting_allocator<debug_allocator<Foo,std::allocator>, std::size_t> allocator_t; |
67 | typedef tbb::concurrent_vector<Foo, allocator_t> vector_t; |
68 | allocator_t::init_counters(); |
69 | for( int old_size=0; old_size<=128; NextSize( old_size ) ) { |
70 | for( int new_size=0; new_size<=1280; NextSize( new_size ) ) { |
71 | size_t count = FooCount; |
72 | vector_t v; |
73 | ASSERT( count==FooCount, NULL ); |
74 | v.assign(old_size/2, Foo() ); |
75 | ASSERT( count+old_size/2==FooCount, NULL ); |
76 | for( int j=0; j<old_size/2; ++j ) |
77 | ASSERT( v[j].state == Foo::CopyInitialized, NULL); |
78 | v.assign(FooIterator(0), FooIterator(old_size)); |
79 | v.resize(new_size, Foo(33) ); |
80 | ASSERT( count+new_size==FooCount, NULL ); |
81 | for( int j=0; j<new_size; ++j ) { |
82 | int expected = j<old_size ? j : 33; |
83 | if( v[j].bar()!=expected ) |
84 | REPORT("ERROR on line %d for old_size=%ld new_size=%ld v[%ld].bar()=%d != %d\n" ,__LINE__,long(old_size),long(new_size),long(j),v[j].bar(), expected); |
85 | } |
86 | ASSERT( v.size()==size_t(new_size), NULL ); |
87 | for( int j=0; j<new_size; ++j ) { |
88 | v[j].bar() = ~j; |
89 | } |
90 | const vector_t& cv = v; |
91 | // Try copy constructor |
92 | vector_t copy_of_v(cv); |
93 | CheckVector(cv,new_size,old_size); |
94 | ASSERT( !(v != copy_of_v), NULL ); |
95 | v.clear(); |
96 | ASSERT( v.empty(), NULL ); |
97 | swap(v, copy_of_v); |
98 | ASSERT( copy_of_v.empty(), NULL ); |
99 | CheckVector(v,new_size,old_size); |
100 | } |
101 | } |
102 | ASSERT( allocator_t::items_allocated == allocator_t::items_freed, NULL); |
103 | ASSERT( allocator_t::allocations == allocator_t::frees, NULL); |
104 | } |
105 | |
106 | //! Test reserve, compact, capacity |
107 | void TestCapacity() { |
108 | typedef static_counting_allocator<debug_allocator<Foo,tbb::cache_aligned_allocator>, std::size_t> allocator_t; |
109 | typedef tbb::concurrent_vector<Foo, allocator_t> vector_t; |
110 | allocator_t::init_counters(); |
111 | for( size_t old_size=0; old_size<=11000; old_size=(old_size<5 ? old_size+1 : 3*old_size) ) { |
112 | for( size_t new_size=0; new_size<=11000; new_size=(new_size<5 ? new_size+1 : 3*new_size) ) { |
113 | size_t count = FooCount; |
114 | { |
115 | vector_t v; v.reserve(old_size); |
116 | ASSERT( v.capacity()>=old_size, NULL ); |
117 | v.reserve( new_size ); |
118 | ASSERT( v.capacity()>=old_size, NULL ); |
119 | ASSERT( v.capacity()>=new_size, NULL ); |
120 | ASSERT( v.empty(), NULL ); |
121 | size_t fill_size = 2*new_size; |
122 | for( size_t i=0; i<fill_size; ++i ) { |
123 | ASSERT( size_t(FooCount)==count+i, NULL ); |
124 | size_t j = v.grow_by(1) - v.begin(); |
125 | ASSERT( j==i, NULL ); |
126 | v[j].bar() = int(~j); |
127 | } |
128 | vector_t copy_of_v(v); // should allocate first segment with same size as for shrink_to_fit() |
129 | if(__TBB_Log2(/*reserved size*/old_size|1) > __TBB_Log2(fill_size|1) ) |
130 | ASSERT( v.capacity() != copy_of_v.capacity(), NULL ); |
131 | v.shrink_to_fit(); |
132 | ASSERT( v.capacity() == copy_of_v.capacity(), NULL ); |
133 | CheckVector(v, new_size*2, old_size); // check vector correctness |
134 | ASSERT( v==copy_of_v, NULL ); // TODO: check also segments layout equality |
135 | } |
136 | ASSERT( FooCount==count, NULL ); |
137 | } |
138 | } |
139 | ASSERT( allocator_t::items_allocated == allocator_t::items_freed, NULL); |
140 | ASSERT( allocator_t::allocations == allocator_t::frees, NULL); |
141 | } |
142 | |
143 | struct AssignElement { |
144 | typedef tbb::concurrent_vector<int>::range_type::iterator iterator; |
145 | iterator base; |
146 | void operator()( const tbb::concurrent_vector<int>::range_type& range ) const { |
147 | for( iterator i=range.begin(); i!=range.end(); ++i ) { |
148 | if( *i!=0 ) |
149 | REPORT("ERROR for v[%ld]\n" , long(i-base)); |
150 | *i = int(i-base); |
151 | } |
152 | } |
153 | AssignElement( iterator base_ ) : base(base_) {} |
154 | }; |
155 | |
156 | struct CheckElement { |
157 | typedef tbb::concurrent_vector<int>::const_range_type::iterator iterator; |
158 | iterator base; |
159 | void operator()( const tbb::concurrent_vector<int>::const_range_type& range ) const { |
160 | for( iterator i=range.begin(); i!=range.end(); ++i ) |
161 | if( *i != int(i-base) ) |
162 | REPORT("ERROR for v[%ld]\n" , long(i-base)); |
163 | } |
164 | CheckElement( iterator base_ ) : base(base_) {} |
165 | }; |
166 | |
167 | #include "tbb/tick_count.h" |
168 | #include "tbb/parallel_for.h" |
169 | #include "harness.h" |
170 | |
171 | //! Problem size |
172 | const size_t N = 500000; |
173 | |
174 | //! Test parallel access by iterators |
175 | void TestParallelFor( int nthread ) { |
176 | typedef tbb::concurrent_vector<int> vector_t; |
177 | vector_t v; |
178 | v.resize(N); |
179 | tbb::tick_count t0 = tbb::tick_count::now(); |
180 | REMARK("Calling parallel_for with %ld threads\n" ,long(nthread)); |
181 | tbb::parallel_for( v.range(10000), AssignElement(v.begin()) ); |
182 | tbb::tick_count t1 = tbb::tick_count::now(); |
183 | const vector_t& u = v; |
184 | tbb::parallel_for( u.range(10000), CheckElement(u.begin()) ); |
185 | tbb::tick_count t2 = tbb::tick_count::now(); |
186 | REMARK("Time for parallel_for: assign time = %8.5f, check time = %8.5f\n" , |
187 | (t1-t0).seconds(),(t2-t1).seconds()); |
188 | for( long i=0; size_t(i)<v.size(); ++i ) |
189 | if( v[i]!=i ) |
190 | REPORT("ERROR for v[%ld]\n" , i); |
191 | } |
192 | |
193 | template<typename Iterator1, typename Iterator2> |
194 | void TestIteratorAssignment( Iterator2 j ) { |
195 | Iterator1 i(j); |
196 | ASSERT( i==j, NULL ); |
197 | ASSERT( !(i!=j), NULL ); |
198 | Iterator1 k; |
199 | k = j; |
200 | ASSERT( k==j, NULL ); |
201 | ASSERT( !(k!=j), NULL ); |
202 | } |
203 | |
204 | template<typename Range1, typename Range2> |
205 | void TestRangeAssignment( Range2 r2 ) { |
206 | Range1 r1(r2); r1 = r2; |
207 | } |
208 | |
209 | template<typename Iterator, typename T> |
210 | void TestIteratorTraits() { |
211 | AssertSameType( static_cast<typename Iterator::difference_type*>(0), static_cast<ptrdiff_t*>(0) ); |
212 | AssertSameType( static_cast<typename Iterator::value_type*>(0), static_cast<T*>(0) ); |
213 | AssertSameType( static_cast<typename Iterator::pointer*>(0), static_cast<T**>(0) ); |
214 | AssertSameType( static_cast<typename Iterator::iterator_category*>(0), static_cast<std::random_access_iterator_tag*>(0) ); |
215 | T x; |
216 | typename Iterator::reference xr = x; |
217 | typename Iterator::pointer xp = &x; |
218 | ASSERT( &xr==xp, NULL ); |
219 | } |
220 | |
221 | template<typename Vector, typename Iterator> |
222 | void CheckConstIterator( const Vector& u, int i, const Iterator& cp ) { |
223 | typename Vector::const_reference pref = *cp; |
224 | if( pref.bar()!=i ) |
225 | REPORT("ERROR for u[%ld] using const_iterator\n" , long(i)); |
226 | typename Vector::difference_type delta = cp-u.begin(); |
227 | ASSERT( delta==i, NULL ); |
228 | if( u[i].bar()!=i ) |
229 | REPORT("ERROR for u[%ld] using subscripting\n" , long(i)); |
230 | ASSERT( u.begin()[i].bar()==i, NULL ); |
231 | } |
232 | |
233 | template<typename Iterator1, typename Iterator2, typename V> |
234 | void CheckIteratorComparison( V& u ) { |
235 | V u2 = u; |
236 | Iterator1 i = u.begin(); |
237 | |
238 | for( int i_count=0; i_count<100; ++i_count ) { |
239 | Iterator2 j = u.begin(); |
240 | Iterator2 i2 = u2.begin(); |
241 | for( int j_count=0; j_count<100; ++j_count ) { |
242 | ASSERT( (i==j)==(i_count==j_count), NULL ); |
243 | ASSERT( (i!=j)==(i_count!=j_count), NULL ); |
244 | ASSERT( (i-j)==(i_count-j_count), NULL ); |
245 | ASSERT( (i<j)==(i_count<j_count), NULL ); |
246 | ASSERT( (i>j)==(i_count>j_count), NULL ); |
247 | ASSERT( (i<=j)==(i_count<=j_count), NULL ); |
248 | ASSERT( (i>=j)==(i_count>=j_count), NULL ); |
249 | ASSERT( !(i==i2), NULL ); |
250 | ASSERT( i!=i2, NULL ); |
251 | ++j; |
252 | ++i2; |
253 | } |
254 | ++i; |
255 | } |
256 | } |
257 | |
258 | template<typename Vector, typename T> |
259 | void TestGrowToAtLeastWithSourceParameter(T const& src){ |
260 | static const size_t vector_size = 10; |
261 | Vector v1(vector_size,src); |
262 | Vector v2; |
263 | v2.grow_to_at_least(vector_size,src); |
264 | ASSERT(v1==v2,"grow_to_at_least(vector_size,src) did not properly initialize new elements ?" ); |
265 | } |
266 | //! Test sequential iterators for vector type V. |
267 | /** Also does timing. */ |
268 | template<typename T> |
269 | void TestSequentialFor() { |
270 | typedef tbb::concurrent_vector<FooWithAssign> V; |
271 | V v(N); |
272 | ASSERT(v.grow_by(0) == v.grow_by(0, FooWithAssign()), NULL); |
273 | |
274 | // Check iterator |
275 | tbb::tick_count t0 = tbb::tick_count::now(); |
276 | typename V::iterator p = v.begin(); |
277 | ASSERT( !(*p).is_const(), NULL ); |
278 | ASSERT( !p->is_const(), NULL ); |
279 | for( int i=0; size_t(i)<v.size(); ++i, ++p ) { |
280 | if( (*p).state!=Foo::DefaultInitialized ) |
281 | REPORT("ERROR for v[%ld]\n" , long(i)); |
282 | typename V::reference pref = *p; |
283 | pref.bar() = i; |
284 | typename V::difference_type delta = p-v.begin(); |
285 | ASSERT( delta==i, NULL ); |
286 | ASSERT( -delta<=0, "difference type not signed?" ); |
287 | } |
288 | tbb::tick_count t1 = tbb::tick_count::now(); |
289 | |
290 | // Check const_iterator going forwards |
291 | const V& u = v; |
292 | typename V::const_iterator cp = u.begin(); |
293 | ASSERT( cp == v.cbegin(), NULL ); |
294 | ASSERT( (*cp).is_const(), NULL ); |
295 | ASSERT( cp->is_const(), NULL ); |
296 | ASSERT( *cp == v.front(), NULL); |
297 | for( int i=0; size_t(i)<u.size(); ++i ) { |
298 | CheckConstIterator(u,i,cp); |
299 | V::const_iterator &cpr = ++cp; |
300 | ASSERT( &cpr == &cp, "pre-increment not returning a reference?" ); |
301 | } |
302 | tbb::tick_count t2 = tbb::tick_count::now(); |
303 | REMARK("Time for serial for: assign time = %8.5f, check time = %8.5f\n" , |
304 | (t1-t0).seconds(),(t2-t1).seconds()); |
305 | |
306 | // Now go backwards |
307 | cp = u.end(); |
308 | ASSERT( cp == v.cend(), NULL ); |
309 | for( int i=int(u.size()); i>0; ) { |
310 | --i; |
311 | V::const_iterator &cpr = --cp; |
312 | ASSERT( &cpr == &cp, "pre-decrement not returning a reference?" ); |
313 | if( i>0 ) { |
314 | typename V::const_iterator cp_old = cp--; |
315 | intptr_t here = (*cp_old).bar(); |
316 | ASSERT( here==u[i].bar(), NULL ); |
317 | typename V::const_iterator cp_new = cp++; |
318 | intptr_t prev = (*cp_new).bar(); |
319 | ASSERT( prev==u[i-1].bar(), NULL ); |
320 | } |
321 | CheckConstIterator(u,i,cp); |
322 | } |
323 | |
324 | // Now go forwards and backwards |
325 | ptrdiff_t k = 0; |
326 | cp = u.begin(); |
327 | for( size_t i=0; i<u.size(); ++i ) { |
328 | CheckConstIterator(u,int(k),cp); |
329 | typename V::difference_type delta = i*3 % u.size(); |
330 | if( 0<=k+delta && size_t(k+delta)<u.size() ) { |
331 | V::const_iterator &cpr = (cp += delta); |
332 | ASSERT( &cpr == &cp, "+= not returning a reference?" ); |
333 | k += delta; |
334 | } |
335 | delta = i*7 % u.size(); |
336 | if( 0<=k-delta && size_t(k-delta)<u.size() ) { |
337 | if( i&1 ) { |
338 | V::const_iterator &cpr = (cp -= delta); |
339 | ASSERT( &cpr == &cp, "-= not returning a reference?" ); |
340 | } else |
341 | cp = cp - delta; // Test operator- |
342 | k -= delta; |
343 | } |
344 | } |
345 | |
346 | for( int i=0; size_t(i)<u.size(); i=(i<50?i+1:i*3) ) |
347 | for( int j=-i; size_t(i+j)<u.size(); j=(j<50?j+1:j*5) ) { |
348 | ASSERT( (u.begin()+i)[j].bar()==i+j, NULL ); |
349 | ASSERT( (v.begin()+i)[j].bar()==i+j, NULL ); |
350 | ASSERT((v.cbegin()+i)[j].bar()==i+j, NULL ); |
351 | ASSERT( (i+u.begin())[j].bar()==i+j, NULL ); |
352 | ASSERT( (i+v.begin())[j].bar()==i+j, NULL ); |
353 | ASSERT((i+v.cbegin())[j].bar()==i+j, NULL ); |
354 | } |
355 | |
356 | CheckIteratorComparison<typename V::iterator, typename V::iterator>(v); |
357 | CheckIteratorComparison<typename V::iterator, typename V::const_iterator>(v); |
358 | CheckIteratorComparison<typename V::const_iterator, typename V::iterator>(v); |
359 | CheckIteratorComparison<typename V::const_iterator, typename V::const_iterator>(v); |
360 | |
361 | TestIteratorAssignment<typename V::const_iterator>( u.begin() ); |
362 | TestIteratorAssignment<typename V::const_iterator>( v.begin() ); |
363 | TestIteratorAssignment<typename V::const_iterator>( v.cbegin() ); |
364 | TestIteratorAssignment<typename V::iterator>( v.begin() ); |
365 | // doesn't compile as expected: TestIteratorAssignment<typename V::iterator>( u.begin() ); |
366 | |
367 | TestRangeAssignment<typename V::const_range_type>( u.range() ); |
368 | TestRangeAssignment<typename V::const_range_type>( v.range() ); |
369 | TestRangeAssignment<typename V::range_type>( v.range() ); |
370 | // doesn't compile as expected: TestRangeAssignment<typename V::range_type>( u.range() ); |
371 | |
372 | // Check reverse_iterator |
373 | typename V::reverse_iterator rp = v.rbegin(); |
374 | for( size_t i=v.size(); i>0; --i, ++rp ) { |
375 | typename V::reference pref = *rp; |
376 | ASSERT( size_t(pref.bar())==i-1, NULL ); |
377 | ASSERT( rp!=v.rend(), NULL ); |
378 | } |
379 | ASSERT( rp==v.rend(), NULL ); |
380 | |
381 | // Check const_reverse_iterator |
382 | typename V::const_reverse_iterator crp = u.rbegin(); |
383 | ASSERT( crp == v.crbegin(), NULL ); |
384 | ASSERT( *crp == v.back(), NULL); |
385 | for( size_t i=v.size(); i>0; --i, ++crp ) { |
386 | typename V::const_reference cpref = *crp; |
387 | ASSERT( size_t(cpref.bar())==i-1, NULL ); |
388 | ASSERT( crp!=u.rend(), NULL ); |
389 | } |
390 | ASSERT( crp == u.rend(), NULL ); |
391 | ASSERT( crp == v.crend(), NULL ); |
392 | |
393 | TestIteratorAssignment<typename V::const_reverse_iterator>( u.rbegin() ); |
394 | TestIteratorAssignment<typename V::reverse_iterator>( v.rbegin() ); |
395 | |
396 | // test compliance with C++ Standard 2003, clause 23.1.1p9 |
397 | { |
398 | tbb::concurrent_vector<int> v1, v2(1, 100); |
399 | v1.assign(1, 100); ASSERT(v1 == v2, NULL); |
400 | ASSERT(v1.size() == 1 && v1[0] == 100, "used integral iterators" ); |
401 | } |
402 | |
403 | // cross-allocator tests |
404 | #if !defined(_WIN64) || defined(_CPPLIB_VER) |
405 | typedef local_counting_allocator<std::allocator<int>, size_t> allocator1_t; |
406 | typedef tbb::cache_aligned_allocator<int> allocator2_t; |
407 | typedef tbb::concurrent_vector<FooWithAssign, allocator1_t> V1; |
408 | typedef tbb::concurrent_vector<FooWithAssign, allocator2_t> V2; |
409 | V1 v1( v ); // checking cross-allocator copying |
410 | V2 v2( 10 ); v2 = v1; // checking cross-allocator assignment |
411 | ASSERT( (v1 == v) && !(v2 != v), NULL); |
412 | ASSERT( !(v1 < v) && !(v2 > v), NULL); |
413 | ASSERT( (v1 <= v) && (v2 >= v), NULL); |
414 | #endif |
415 | } |
416 | |
417 | namespace test_grow_to_at_least_helpers { |
418 | template<typename MyVector > |
419 | class GrowToAtLeast: NoAssign { |
420 | typedef typename MyVector::const_reference const_reference; |
421 | |
422 | const bool my_use_two_args_form ; |
423 | MyVector& my_vector; |
424 | const_reference my_init_from; |
425 | public: |
426 | void operator()( const tbb::blocked_range<size_t>& range ) const { |
427 | for( size_t i=range.begin(); i!=range.end(); ++i ) { |
428 | size_t n = my_vector.size(); |
429 | size_t req = (i % (2*n+1))+1; |
430 | |
431 | typename MyVector::iterator p; |
432 | Foo::State desired_state; |
433 | if (my_use_two_args_form){ |
434 | p = my_vector.grow_to_at_least(req,my_init_from); |
435 | desired_state = Foo::CopyInitialized; |
436 | }else{ |
437 | p = my_vector.grow_to_at_least(req); |
438 | desired_state = Foo::DefaultInitialized; |
439 | } |
440 | if( p-my_vector.begin() < typename MyVector::difference_type(req) ) |
441 | ASSERT( p->state == desired_state || p->state == Foo::ZeroInitialized, NULL ); |
442 | ASSERT( my_vector.size()>=req, NULL ); |
443 | } |
444 | } |
445 | GrowToAtLeast(bool use_two_args_form, MyVector& vector, const_reference init_from ) |
446 | : my_use_two_args_form(use_two_args_form), my_vector(vector), my_init_from(init_from) {} |
447 | }; |
448 | } |
449 | |
450 | template<bool use_two_arg_form> |
451 | void TestConcurrentGrowToAtLeastImpl() { |
452 | using namespace test_grow_to_at_least_helpers; |
453 | typedef static_counting_allocator< tbb::zero_allocator<Foo> > MyAllocator; |
454 | typedef tbb::concurrent_vector<Foo, MyAllocator> MyVector; |
455 | Foo copy_from; |
456 | MyAllocator::init_counters(); |
457 | MyVector v(2, Foo(), MyAllocator()); |
458 | for( size_t s=1; s<1000; s*=10 ) { |
459 | tbb::parallel_for( tbb::blocked_range<size_t>(0,10000*s,s), GrowToAtLeast<MyVector>(use_two_arg_form, v, copy_from), tbb::simple_partitioner() ); |
460 | } |
461 | v.clear(); |
462 | ASSERT( 0 == v.get_allocator().frees, NULL); |
463 | v.shrink_to_fit(); |
464 | size_t items_allocated = v.get_allocator().items_allocated, |
465 | items_freed = v.get_allocator().items_freed; |
466 | size_t allocations = v.get_allocator().allocations, |
467 | frees = v.get_allocator().frees; |
468 | ASSERT( items_allocated == items_freed, NULL); |
469 | ASSERT( allocations == frees, NULL); |
470 | } |
471 | |
472 | void TestConcurrentGrowToAtLeast() { |
473 | TestConcurrentGrowToAtLeastImpl<false>(); |
474 | TestConcurrentGrowToAtLeastImpl<true>(); |
475 | } |
476 | |
477 | struct grain_map: NoAssign { |
478 | enum grow_method_enum { |
479 | grow_by_range = 1, |
480 | grow_by_default, |
481 | grow_by_copy, |
482 | grow_by_init_list, |
483 | push_back, |
484 | push_back_move, |
485 | emplace_back, |
486 | last_method |
487 | }; |
488 | |
489 | struct range_part { |
490 | size_t number_of_parts; |
491 | grain_map::grow_method_enum method; |
492 | bool distribute; |
493 | Foo::State expected_element_state; |
494 | }; |
495 | |
496 | const std::vector<range_part> distributed; |
497 | const std::vector<range_part> batched; |
498 | const size_t total_number_of_parts; |
499 | |
500 | grain_map(const range_part* begin, const range_part* end) |
501 | : distributed(separate(begin,end, &distributed::is_not)) |
502 | , batched(separate(begin,end, &distributed::is_yes)) |
503 | , total_number_of_parts(std::accumulate(begin, end, (size_t)0, &sum_number_of_parts::sum)) |
504 | {} |
505 | |
506 | private: |
507 | struct sum_number_of_parts{ |
508 | static size_t sum(size_t accumulator, grain_map::range_part const& rp){ return accumulator + rp.number_of_parts;} |
509 | }; |
510 | |
511 | template <typename functor_t> |
512 | static std::vector<range_part> separate(const range_part* begin, const range_part* end, functor_t f){ |
513 | std::vector<range_part> part; |
514 | part.reserve(std::distance(begin,end)); |
515 | //copy all that false==f(*it) |
516 | std::remove_copy_if(begin, end, std::back_inserter(part), f); |
517 | |
518 | return part; |
519 | } |
520 | |
521 | struct distributed { |
522 | static bool is_not(range_part const& rp){ return !rp.distribute;} |
523 | static bool is_yes(range_part const& rp){ return rp.distribute;} |
524 | }; |
525 | }; |
526 | |
527 | //! Test concurrent invocations of method concurrent_vector::grow_by |
528 | template<typename MyVector> |
529 | class GrowBy: NoAssign { |
530 | MyVector& my_vector; |
531 | const grain_map& my_grain_map; |
532 | size_t my_part_weight; |
533 | public: |
534 | void operator()( const tbb::blocked_range<size_t>& range ) const { |
535 | ASSERT( range.begin() < range.end(), NULL ); |
536 | |
537 | size_t current_adding_index_in_cvector = range.begin(); |
538 | |
539 | for(size_t index=0; index < my_grain_map.batched.size(); ++index){ |
540 | const grain_map::range_part& batch_part = my_grain_map.batched[index]; |
541 | const size_t number_of_items_to_add = batch_part.number_of_parts * my_part_weight; |
542 | const size_t end = current_adding_index_in_cvector + number_of_items_to_add; |
543 | |
544 | switch(batch_part.method){ |
545 | case grain_map::grow_by_range : { |
546 | my_vector.grow_by(FooIterator(current_adding_index_in_cvector),FooIterator(end)); |
547 | } break; |
548 | case grain_map::grow_by_default : { |
549 | typename MyVector::iterator const s = my_vector.grow_by(number_of_items_to_add); |
550 | for( size_t k = 0; k < number_of_items_to_add; ++k ) |
551 | s[k].bar() = current_adding_index_in_cvector + k; |
552 | } break; |
553 | #if __TBB_INITIALIZER_LISTS_PRESENT |
554 | case grain_map::grow_by_init_list : { |
555 | FooIterator curr(current_adding_index_in_cvector); |
556 | for ( size_t k = 0; k < number_of_items_to_add; ++k ) { |
557 | if ( k + 4 < number_of_items_to_add ) { |
558 | my_vector.grow_by( { *curr++, *curr++, *curr++, *curr++, *curr++ } ); |
559 | k += 4; |
560 | } else { |
561 | my_vector.grow_by( { *curr++ } ); |
562 | } |
563 | } |
564 | ASSERT( curr == FooIterator(end), NULL ); |
565 | } break; |
566 | #endif |
567 | default : { ASSERT(false, "using unimplemented method of batch add in ConcurrentGrow test." );} break; |
568 | }; |
569 | |
570 | current_adding_index_in_cvector = end; |
571 | } |
572 | |
573 | std::vector<size_t> items_left_to_add(my_grain_map.distributed.size()); |
574 | for (size_t i=0; i<my_grain_map.distributed.size(); ++i ){ |
575 | items_left_to_add[i] = my_grain_map.distributed[i].number_of_parts * my_part_weight; |
576 | } |
577 | |
578 | for (;current_adding_index_in_cvector < range.end(); ++current_adding_index_in_cvector){ |
579 | size_t method_index = current_adding_index_in_cvector % my_grain_map.distributed.size(); |
580 | |
581 | if (! items_left_to_add[method_index]) { |
582 | struct not_zero{ |
583 | static bool is(size_t items_to_add){ return items_to_add;} |
584 | }; |
585 | method_index = std::distance(items_left_to_add.begin(), std::find_if(items_left_to_add.begin(), items_left_to_add.end(), ¬_zero::is)); |
586 | ASSERT(method_index < my_grain_map.distributed.size(), "incorrect test setup - wrong expected distribution: left free space but no elements to add?" ); |
587 | }; |
588 | |
589 | ASSERT(items_left_to_add[method_index], "logic error ?" ); |
590 | const grain_map::range_part& distributed_part = my_grain_map.distributed[method_index]; |
591 | |
592 | typename MyVector::iterator r; |
593 | typename MyVector::value_type source; |
594 | source.bar() = current_adding_index_in_cvector; |
595 | |
596 | switch(distributed_part.method){ |
597 | case grain_map::grow_by_default : { |
598 | (r = my_vector.grow_by(1))->bar() = current_adding_index_in_cvector; |
599 | } break; |
600 | case grain_map::grow_by_copy : { |
601 | r = my_vector.grow_by(1, source); |
602 | } break; |
603 | case grain_map::push_back : { |
604 | r = my_vector.push_back(source); |
605 | } break; |
606 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
607 | case grain_map::push_back_move : { |
608 | r = my_vector.push_back(std::move(source)); |
609 | } break; |
610 | #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
611 | case grain_map::emplace_back : { |
612 | r = my_vector.emplace_back(current_adding_index_in_cvector); |
613 | } break; |
614 | #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
615 | #endif //__TBB_CPP11_RVALUE_REF_PRESENT |
616 | |
617 | default : { ASSERT(false, "using unimplemented method of batch add in ConcurrentGrow test." );} break; |
618 | }; |
619 | |
620 | ASSERT( static_cast<size_t>(r->bar()) == current_adding_index_in_cvector, NULL ); |
621 | } |
622 | } |
623 | |
624 | GrowBy( MyVector& vector, const grain_map& m, size_t part_weight ) |
625 | : my_vector(vector) |
626 | , my_grain_map(m) |
627 | , my_part_weight(part_weight) |
628 | { |
629 | } |
630 | }; |
631 | |
632 | const grain_map::range_part concurrent_grow_single_range_map [] = { |
633 | // number_of_parts, method, distribute, expected_element_state |
634 | {3, grain_map::grow_by_range, false, |
635 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
636 | Foo::MoveInitialized |
637 | #else |
638 | Foo::CopyInitialized |
639 | #endif |
640 | }, |
641 | #if __TBB_INITIALIZER_LISTS_PRESENT && !__TBB_CPP11_INIT_LIST_TEMP_OBJS_LIFETIME_BROKEN |
642 | {1, grain_map::grow_by_init_list, false, Foo::CopyInitialized}, |
643 | #endif |
644 | {2, grain_map::grow_by_default, false, Foo::DefaultInitialized}, |
645 | {1, grain_map::grow_by_default, true, Foo::DefaultInitialized}, |
646 | {1, grain_map::grow_by_copy, true, Foo::CopyInitialized}, |
647 | {1, grain_map::push_back, true, Foo::CopyInitialized}, |
648 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
649 | {1, grain_map::push_back_move, true, Foo::MoveInitialized}, |
650 | #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
651 | {1, grain_map::emplace_back, true, Foo::DirectInitialized}, |
652 | #endif // __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
653 | #endif //__TBB_CPP11_RVALUE_REF_PRESENT |
654 | }; |
655 | |
656 | //! Test concurrent invocations of grow methods |
657 | void TestConcurrentGrowBy( int nthread ) { |
658 | |
659 | typedef static_counting_allocator<debug_allocator<Foo> > MyAllocator; |
660 | typedef tbb::concurrent_vector<Foo, MyAllocator> MyVector; |
661 | |
662 | #if __TBB_INITIALIZER_LISTS_PRESENT && __TBB_CPP11_INIT_LIST_TEMP_OBJS_LIFETIME_BROKEN |
663 | static bool is_reported = false; |
664 | if ( !is_reported ) { |
665 | REPORT( "Known issue: concurrent tests of grow_by(std::initializer_list) are skipped.\n" ); |
666 | is_reported = true; |
667 | } |
668 | #endif |
669 | |
670 | MyAllocator::init_counters(); |
671 | { |
672 | grain_map m(concurrent_grow_single_range_map, Harness::end(concurrent_grow_single_range_map)); |
673 | |
674 | static const size_t desired_grain_size = 100; |
675 | |
676 | static const size_t part_weight = desired_grain_size / m.total_number_of_parts; |
677 | static const size_t grain_size = part_weight * m.total_number_of_parts; |
678 | static const size_t number_of_grains = 8; //this should be (power of two) in order to get minimal ranges equal to grain_size |
679 | static const size_t range_size = grain_size * number_of_grains; |
680 | |
681 | MyAllocator a; |
682 | MyVector v( a ); |
683 | tbb::parallel_for( tbb::blocked_range<size_t>(0,range_size,grain_size), GrowBy<MyVector>(v, m, part_weight), tbb::simple_partitioner() ); |
684 | ASSERT( v.size()==size_t(range_size), NULL ); |
685 | |
686 | // Verify that v is a permutation of 0..m |
687 | size_t inversions = 0, direct_inits = 0, def_inits = 0, copy_inits = 0, move_inits = 0; |
688 | std::vector<bool> found(range_size, 0); |
689 | for( size_t i=0; i<range_size; ++i ) { |
690 | if( v[i].state == Foo::DefaultInitialized ) ++def_inits; |
691 | else if( v[i].state == Foo::DirectInitialized ) ++direct_inits; |
692 | else if( v[i].state == Foo::CopyInitialized ) ++copy_inits; |
693 | else if( v[i].state == Foo::MoveInitialized ) ++move_inits; |
694 | else { |
695 | REMARK("i: %d " , i); |
696 | ASSERT( false, "v[i] seems not initialized" ); |
697 | } |
698 | intptr_t index = v[i].bar(); |
699 | ASSERT( !found[index], NULL ); |
700 | found[index] = true; |
701 | if( i>0 ) |
702 | inversions += v[i].bar()<v[i-1].bar(); |
703 | } |
704 | for( size_t i=0; i<range_size; ++i ) { |
705 | ASSERT( found[i], NULL ); |
706 | ASSERT( nthread>1 || v[i].bar() == static_cast<intptr_t>(i), "sequential execution is wrong" ); |
707 | } |
708 | |
709 | REMARK("Initialization by default constructor: %d, by copy: %d, by move: %d\n" , def_inits, copy_inits, move_inits); |
710 | |
711 | size_t expected_direct_inits = 0, expected_def_inits = 0, expected_copy_inits = 0, expected_move_inits = 0; |
712 | for (size_t i=0; i<Harness::array_length(concurrent_grow_single_range_map); ++i){ |
713 | const grain_map::range_part& rp =concurrent_grow_single_range_map[i]; |
714 | switch (rp.expected_element_state){ |
715 | case Foo::DefaultInitialized: { expected_def_inits += rp.number_of_parts ; } break; |
716 | case Foo::DirectInitialized: { expected_direct_inits += rp.number_of_parts ;} break; |
717 | case Foo::MoveInitialized: { expected_move_inits += rp.number_of_parts ;} break; |
718 | case Foo::CopyInitialized: { expected_copy_inits += rp.number_of_parts ;} break; |
719 | default: {ASSERT(false, "unexpected expected state" );}break; |
720 | }; |
721 | } |
722 | |
723 | expected_def_inits *= part_weight * number_of_grains; |
724 | expected_move_inits *= part_weight * number_of_grains; |
725 | expected_copy_inits *= part_weight * number_of_grains; |
726 | expected_direct_inits *= part_weight * number_of_grains; |
727 | |
728 | ASSERT( def_inits == expected_def_inits , NULL); |
729 | ASSERT( copy_inits == expected_copy_inits , NULL); |
730 | ASSERT( move_inits == expected_move_inits , NULL); |
731 | ASSERT( direct_inits == expected_direct_inits , NULL); |
732 | |
733 | if( nthread>1 && inversions<range_size/20 ) |
734 | REPORT("Warning: not much concurrency in TestConcurrentGrowBy (%d inversions)\n" , inversions); |
735 | } |
736 | //TODO: factor this into separate thing, as it seems to used in big number of tests |
737 | size_t items_allocated = MyAllocator::items_allocated, |
738 | items_freed = MyAllocator::items_freed; |
739 | size_t allocations = MyAllocator::allocations, |
740 | frees = MyAllocator::frees; |
741 | ASSERT( items_allocated == items_freed, NULL); |
742 | ASSERT( allocations == frees, NULL); |
743 | } |
744 | |
745 | template <typename Vector> |
746 | void test_grow_by_empty_range( Vector &v, typename Vector::value_type* range_begin_end ) { |
747 | const Vector v_copy = v; |
748 | ASSERT( v.grow_by( range_begin_end, range_begin_end ) == v.end(), "grow_by(empty_range) returned a wrong iterator." ); |
749 | ASSERT( v == v_copy, "grow_by(empty_range) has changed the vector." ); |
750 | } |
751 | |
752 | void TestSerialGrowByRange( bool fragmented_vector ) { |
753 | tbb::concurrent_vector<int> v; |
754 | if ( fragmented_vector ) { |
755 | v.reserve( 1 ); |
756 | } |
757 | int init_range[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; |
758 | ASSERT( v.grow_by( init_range, init_range + (Harness::array_length( init_range )) ) == v.begin(), "grow_by(I,I) returned a wrong iterator." ); |
759 | ASSERT( std::equal( v.begin(), v.end(), init_range ), "grow_by(I,I) did not properly copied all elements ?" ); |
760 | test_grow_by_empty_range( v, init_range ); |
761 | test_grow_by_empty_range( v, (int*)NULL ); |
762 | } |
763 | |
764 | //TODO: move this to more appropriate place, smth like test_harness.cpp |
765 | void TestArrayLength(){ |
766 | int five_element_array[5] = {0}; |
767 | ASSERT(Harness::array_length(five_element_array)==5,"array_length failed to determine length of non empty non dynamic array" ); |
768 | } |
769 | |
770 | #if __TBB_INITIALIZER_LISTS_PRESENT |
771 | #include "test_initializer_list.h" |
772 | |
773 | struct test_grow_by { |
774 | template<typename container_type, typename element_type> |
775 | static void do_test( std::initializer_list<element_type> const& il, container_type const& expected ) { |
776 | container_type vd; |
777 | vd.grow_by( il ); |
778 | ASSERT( vd == expected, "grow_by with an initializer list failed" ); |
779 | } |
780 | }; |
781 | |
782 | void TestInitList() { |
783 | REMARK( "testing initializer_list methods \n" ); |
784 | using namespace initializer_list_support_tests; |
785 | TestInitListSupport<tbb::concurrent_vector<char>, test_grow_by>( { 1, 2, 3, 4, 5 } ); |
786 | TestInitListSupport<tbb::concurrent_vector<int>, test_grow_by>( {} ); |
787 | } |
788 | #endif //if __TBB_INITIALIZER_LISTS_PRESENT |
789 | |
790 | #if __TBB_RANGE_BASED_FOR_PRESENT |
791 | #include "test_range_based_for.h" |
792 | |
793 | void TestRangeBasedFor(){ |
794 | using namespace range_based_for_support_tests; |
795 | |
796 | REMARK("testing range based for loop compatibility \n" ); |
797 | typedef tbb::concurrent_vector<int> c_vector; |
798 | c_vector a_c_vector; |
799 | |
800 | const int sequence_length = 100; |
801 | for (int i =1; i<= sequence_length; ++i){ |
802 | a_c_vector.push_back(i); |
803 | } |
804 | |
805 | ASSERT( range_based_for_accumulate(a_c_vector, std::plus<int>(), 0) == gauss_summ_of_int_sequence(sequence_length), "incorrect accumulated value generated via range based for ?" ); |
806 | } |
807 | #endif //if __TBB_RANGE_BASED_FOR_PRESENT |
808 | |
809 | #if TBB_USE_EXCEPTIONS |
810 | #endif //TBB_USE_EXCEPTIONS |
811 | |
812 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
813 | namespace move_semantics_helpers{ |
814 | struct move_only_type:NoCopy{ |
815 | const int* my_pointer; |
816 | move_only_type(move_only_type && other): my_pointer(other.my_pointer){other.my_pointer=NULL;} |
817 | explicit move_only_type(const int* value): my_pointer(value) {} |
818 | }; |
819 | } |
820 | |
821 | void TestPushBackMoveOnlyContainee(){ |
822 | using namespace move_semantics_helpers; |
823 | typedef tbb::concurrent_vector<move_only_type > vector_t; |
824 | vector_t v; |
825 | static const int magic_number =7; |
826 | move_only_type src(&magic_number); |
827 | v.push_back(std::move(src)); |
828 | ASSERT(v[0].my_pointer == &magic_number,"item was incorrectly moved during push_back?" ); |
829 | ASSERT(src.my_pointer == NULL,"item was incorrectly moved during push_back?" ); |
830 | } |
831 | |
832 | namespace emplace_helpers{ |
833 | struct wrapper_type:NoCopy{ |
834 | int value1; |
835 | int value2; |
836 | explicit wrapper_type(int v1, int v2) : value1 (v1), value2(v2) {} |
837 | friend bool operator==(const wrapper_type& lhs, const wrapper_type& rhs){ |
838 | return (lhs.value1 == rhs.value1) && (lhs.value2 == rhs.value2 ); |
839 | } |
840 | }; |
841 | } |
842 | #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
843 | //TODO: extend the test to number of types e.g. std::string |
844 | void TestEmplaceBack(){ |
845 | using namespace emplace_helpers; |
846 | typedef tbb::concurrent_vector<wrapper_type > vector_t; |
847 | vector_t v; |
848 | v.emplace_back(1,2); |
849 | ASSERT(v[0] == wrapper_type(1,2),"incorrectly in-place constructed item during emplace_back?" ); |
850 | } |
851 | #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
852 | #endif //__TBB_CPP11_RVALUE_REF_PRESENT |
853 | |
854 | //! Test the assignment operator and swap |
855 | void TestAssign() { |
856 | typedef tbb::concurrent_vector<FooWithAssign, local_counting_allocator<std::allocator<FooWithAssign>, size_t > > vector_t; |
857 | local_counting_allocator<std::allocator<FooWithAssign>, size_t > init_alloc; |
858 | init_alloc.allocations = 100; |
859 | for( int dst_size=1; dst_size<=128; NextSize( dst_size ) ) { |
860 | for( int src_size=2; src_size<=128; NextSize( src_size ) ) { |
861 | vector_t u(FooIterator(0), FooIterator(src_size), init_alloc); |
862 | for( int i=0; i<src_size; ++i ) |
863 | ASSERT( u[i].bar()==i, NULL ); |
864 | vector_t v(dst_size, FooWithAssign(), init_alloc); |
865 | for( int i=0; i<dst_size; ++i ) { |
866 | ASSERT( v[i].state==Foo::CopyInitialized, NULL ); |
867 | v[i].bar() = ~i; |
868 | } |
869 | ASSERT( v != u, NULL); |
870 | v.swap(u); |
871 | CheckVector(u, dst_size, src_size); |
872 | u.swap(v); |
873 | // using assignment |
874 | v = u; |
875 | ASSERT( v == u, NULL); |
876 | u.clear(); |
877 | ASSERT( u.size()==0, NULL ); |
878 | ASSERT( v.size()==size_t(src_size), NULL ); |
879 | for( int i=0; i<src_size; ++i ) |
880 | ASSERT( v[i].bar()==i, NULL ); |
881 | ASSERT( 0 == u.get_allocator().frees, NULL); |
882 | u.shrink_to_fit(); // deallocate unused memory |
883 | size_t items_allocated = u.get_allocator().items_allocated, |
884 | items_freed = u.get_allocator().items_freed; |
885 | size_t allocations = u.get_allocator().allocations, |
886 | frees = u.get_allocator().frees + 100; |
887 | ASSERT( items_allocated == items_freed, NULL); |
888 | ASSERT( allocations == frees, NULL); |
889 | } |
890 | } |
891 | } |
892 | |
893 | struct c_vector_type : default_container_traits { |
894 | template<typename element_type, typename allocator_type> |
895 | struct apply{ |
896 | typedef tbb::concurrent_vector<element_type, allocator_type > type; |
897 | }; |
898 | |
899 | typedef FooIterator init_iterator_type; |
900 | enum{ expected_number_of_items_to_allocate_for_steal_move = 0 }; |
901 | |
902 | template<typename element_type, typename allocator_type, typename iterator> |
903 | static bool equal(tbb::concurrent_vector<element_type, allocator_type > const& c, iterator begin, iterator end){ |
904 | bool equal_sizes = (size_t)std::distance(begin, end) == c.size(); |
905 | return equal_sizes && std::equal(c.begin(), c.end(), begin); |
906 | } |
907 | }; |
908 | |
909 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
910 | void TestSerialGrowByWithMoveIterators(){ |
911 | typedef default_stateful_fixture_make_helper<c_vector_type>::type fixture_t; |
912 | typedef fixture_t::container_t vector_t; |
913 | |
914 | fixture_t fixture("TestSerialGrowByWithMoveIterators" ); |
915 | |
916 | vector_t dst(fixture.dst_allocator); |
917 | dst.grow_by(std::make_move_iterator(fixture.source.begin()), std::make_move_iterator(fixture.source.end())); |
918 | |
919 | fixture.verify_content_deep_moved(dst); |
920 | } |
921 | |
922 | #if __TBB_MOVE_IF_NOEXCEPT_PRESENT |
923 | namespace test_move_in_shrink_to_fit_helpers { |
924 | struct dummy : Harness::StateTrackable<>{ |
925 | int i; |
926 | dummy(int an_i) __TBB_NOEXCEPT(true) : Harness::StateTrackable<>(0), i(an_i) {} |
927 | #if !__TBB_IMPLICIT_MOVE_PRESENT || __TBB_NOTHROW_MOVE_MEMBERS_IMPLICIT_GENERATION_BROKEN |
928 | dummy(const dummy &src) __TBB_NOEXCEPT(true) : Harness::StateTrackable<>(src), i(src.i) {} |
929 | dummy(dummy &&src) __TBB_NOEXCEPT(true) : Harness::StateTrackable<>(std::move(src)), i(src.i) {} |
930 | |
931 | dummy& operator=(dummy &&src) __TBB_NOEXCEPT(true) { |
932 | Harness::StateTrackable<>::operator=(std::move(src)); |
933 | i = src.i; |
934 | return *this; |
935 | } |
936 | |
937 | //somehow magically this declaration make std::is_nothrow_move_constructible<pod>::value to works correctly on icc14+msvc2013 |
938 | ~dummy() __TBB_NOEXCEPT(true) {} |
939 | #endif //!__TBB_IMPLICIT_MOVE_PRESENT || __TBB_NOTHROW_MOVE_MEMBERS_IMPLICIT_GENERATION_BROKEN |
940 | friend bool operator== (const dummy &lhs, const dummy &rhs){ return lhs.i == rhs.i; } |
941 | }; |
942 | } |
943 | void TestSerialMoveInShrinkToFit(){ |
944 | const char* test_name = "TestSerialMoveInShrinkToFit" ; |
945 | REMARK("running %s \n" , test_name); |
946 | using test_move_in_shrink_to_fit_helpers::dummy; |
947 | |
948 | __TBB_STATIC_ASSERT(std::is_nothrow_move_constructible<dummy>::value,"incorrect test setup or broken configuration?" ); |
949 | { |
950 | dummy src(0); |
951 | ASSERT_IN_TEST(is_state<Harness::StateTrackableBase::MoveInitialized>(dummy(std::move_if_noexcept(src))),"broken configuration ?" , test_name); |
952 | } |
953 | static const size_t sequence_size = 15; |
954 | typedef tbb::concurrent_vector<dummy> c_vector_t; |
955 | std::vector<dummy> source(sequence_size, 0); |
956 | std::generate_n(source.begin(), source.size(), std::rand); |
957 | |
958 | c_vector_t c_vector; |
959 | c_vector.reserve(1); //make it fragmented |
960 | |
961 | c_vector.assign(source.begin(), source.end()); |
962 | memory_locations c_vector_before_shrink(c_vector); |
963 | c_vector.shrink_to_fit(); |
964 | |
965 | ASSERT_IN_TEST(c_vector_before_shrink.content_location_changed(c_vector), "incorrect test setup? shrink_to_fit should cause moving elements to other memory locations while it is not" , test_name); |
966 | ASSERT_IN_TEST(all_of(c_vector, is_state_f<Harness::StateTrackableBase::MoveInitialized>()), "container did not move construct some elements?" , test_name); |
967 | ASSERT_IN_TEST(c_vector == c_vector_t(source.begin(),source.end()),"" ,test_name); |
968 | } |
969 | #endif //__TBB_MOVE_IF_NOEXCEPT_PRESENT |
970 | #endif //__TBB_CPP11_RVALUE_REF_PRESENT |
971 | |
972 | #include <string> |
973 | |
974 | // Test the comparison operators |
975 | void TestComparison() { |
976 | std::string str[3]; str[0] = "abc" ; |
977 | str[1].assign("cba" ); |
978 | str[2].assign("abc" ); // same as 0th |
979 | tbb::concurrent_vector<char> var[3]; |
980 | var[0].assign(str[0].begin(), str[0].end()); |
981 | var[1].assign(str[0].rbegin(), str[0].rend()); |
982 | var[2].assign(var[1].rbegin(), var[1].rend()); // same as 0th |
983 | for (int i = 0; i < 3; ++i) { |
984 | for (int j = 0; j < 3; ++j) { |
985 | ASSERT( (var[i] == var[j]) == (str[i] == str[j]), NULL ); |
986 | ASSERT( (var[i] != var[j]) == (str[i] != str[j]), NULL ); |
987 | ASSERT( (var[i] < var[j]) == (str[i] < str[j]), NULL ); |
988 | ASSERT( (var[i] > var[j]) == (str[i] > str[j]), NULL ); |
989 | ASSERT( (var[i] <= var[j]) == (str[i] <= str[j]), NULL ); |
990 | ASSERT( (var[i] >= var[j]) == (str[i] >= str[j]), NULL ); |
991 | } |
992 | } |
993 | } |
994 | |
995 | //------------------------------------------------------------------------ |
996 | // Regression test for problem where on oversubscription caused |
997 | // concurrent_vector::grow_by to run very slowly (TR#196). |
998 | //------------------------------------------------------------------------ |
999 | |
1000 | #include "tbb/task_scheduler_init.h" |
1001 | #include <math.h> |
1002 | |
1003 | typedef unsigned long Number; |
1004 | |
1005 | static tbb::concurrent_vector<Number> Primes; |
1006 | |
1007 | class FindPrimes { |
1008 | bool is_prime( Number val ) const { |
1009 | int limit, factor = 3; |
1010 | if( val<5u ) |
1011 | return val==2; |
1012 | else { |
1013 | limit = long(sqrtf(float(val))+0.5f); |
1014 | while( factor<=limit && val % factor ) |
1015 | ++factor; |
1016 | return factor>limit; |
1017 | } |
1018 | } |
1019 | public: |
1020 | void operator()( const tbb::blocked_range<Number>& r ) const { |
1021 | for( Number i=r.begin(); i!=r.end(); ++i ) { |
1022 | if( i%2 && is_prime(i) ) { |
1023 | Primes.push_back( i ); |
1024 | } |
1025 | } |
1026 | } |
1027 | }; |
1028 | |
1029 | double TimeFindPrimes( int nthread ) { |
1030 | Primes.clear(); |
1031 | Primes.reserve(1000000);// TODO: or compact()? |
1032 | tbb::task_scheduler_init init(nthread); |
1033 | tbb::tick_count t0 = tbb::tick_count::now(); |
1034 | tbb::parallel_for( tbb::blocked_range<Number>(0,1000000,500), FindPrimes() ); |
1035 | tbb::tick_count t1 = tbb::tick_count::now(); |
1036 | return (t1-t0).seconds(); |
1037 | } |
1038 | |
1039 | void TestFindPrimes() { |
1040 | // Time fully subscribed run. |
1041 | double t2 = TimeFindPrimes( tbb::task_scheduler_init::automatic ); |
1042 | |
1043 | // Time parallel run that is very likely oversubscribed. |
1044 | double t128 = TimeFindPrimes(128); |
1045 | REMARK("TestFindPrimes: t2==%g t128=%g k=%g\n" , t2, t128, t128/t2); |
1046 | |
1047 | // We allow the 128-thread run a little extra time to allow for thread overhead. |
1048 | // Theoretically, following test will fail on machine with >128 processors. |
1049 | // But that situation is not going to come up in the near future, |
1050 | // and the generalization to fix the issue is not worth the trouble. |
1051 | if( t128 > 1.3*t2 ) { |
1052 | REPORT("Warning: grow_by is pathetically slow: t2==%g t128=%g k=%g\n" , t2, t128, t128/t2); |
1053 | } |
1054 | } |
1055 | |
1056 | //------------------------------------------------------------------------ |
1057 | // Test compatibility with STL sort. |
1058 | //------------------------------------------------------------------------ |
1059 | |
1060 | #include <algorithm> |
1061 | |
1062 | void TestSort() { |
1063 | for( int n=0; n<100; n=n*3+1 ) { |
1064 | tbb::concurrent_vector<int> array(n); |
1065 | for( int i=0; i<n; ++i ) |
1066 | array.at(i) = (i*7)%n; |
1067 | std::sort( array.begin(), array.end() ); |
1068 | for( int i=0; i<n; ++i ) |
1069 | ASSERT( array[i]==i, NULL ); |
1070 | } |
1071 | } |
1072 | |
1073 | #if TBB_USE_EXCEPTIONS |
1074 | |
1075 | template<typename c_vector> |
1076 | size_t get_early_size(c_vector & v){ |
1077 | return v.grow_by(0) - v.begin(); |
1078 | } |
1079 | |
1080 | void verify_c_vector_size(size_t size, size_t capacity, size_t early_size, const char * const test_name){ |
1081 | ASSERT_IN_TEST( size <= capacity, "" , test_name); |
1082 | ASSERT_IN_TEST( early_size >= size, "" , test_name); |
1083 | } |
1084 | |
1085 | template<typename c_vector_t> |
1086 | void verify_c_vector_size(c_vector_t & c_v, const char * const test_name){ |
1087 | verify_c_vector_size(c_v.size(), c_v.capacity(), get_early_size(c_v), test_name); |
1088 | } |
1089 | |
1090 | void verify_c_vector_capacity_is_below(size_t capacity, size_t high, const char * const test_name){ |
1091 | ASSERT_IN_TEST(capacity > 0, "unexpected capacity" , test_name); |
1092 | ASSERT_IN_TEST(capacity < high, "unexpected capacity" , test_name); |
1093 | } |
1094 | |
1095 | template<typename vector_t> |
1096 | void verify_last_segment_allocation_failed(vector_t const& victim, const char* const test_name){ |
1097 | ASSERT_THROWS_IN_TEST(victim.at(victim.size()), std::range_error, "" ,test_name ); |
1098 | } |
1099 | |
1100 | template<typename vector_t> |
1101 | void verify_assignment_operator_throws_bad_last_alloc(vector_t & victim, const char* const test_name){ |
1102 | vector_t copy_of_victim(victim, victim.get_allocator()); |
1103 | ASSERT_THROWS_IN_TEST(victim = copy_of_victim, tbb::bad_last_alloc, "" , test_name); |
1104 | } |
1105 | |
1106 | template<typename vector_t> |
1107 | void verify_copy_and_assign_from_produce_the_same(vector_t const& victim, const char* const test_name){ |
1108 | //TODO: remove explicit copy of allocator when full support of C++11 allocator_traits in concurrent_vector is present |
1109 | vector_t copy_of_victim(victim, victim.get_allocator()); |
1110 | ASSERT_IN_TEST(copy_of_victim == victim, "copy doesn't match original" , test_name); |
1111 | vector_t copy_of_victim2(10, victim[0], victim.get_allocator()); |
1112 | copy_of_victim2 = victim; |
1113 | ASSERT_IN_TEST(copy_of_victim == copy_of_victim2, "assignment doesn't match copying" , test_name); |
1114 | } |
1115 | |
1116 | template<typename allocator_t> |
1117 | void verify_vector_partially_copied( |
1118 | tbb::concurrent_vector<FooWithAssign, allocator_t> const& victim, size_t planned_victim_size, |
1119 | tbb::concurrent_vector<FooWithAssign, allocator_t> const& src, bool is_memory_allocation_failure ,const char* const test_name) |
1120 | { |
1121 | if (is_memory_allocation_failure) { // allocator generated exception |
1122 | typedef tbb::concurrent_vector<FooWithAssign, allocator_t> vector_t; |
1123 | ASSERT_IN_TEST( victim == vector_t(src.begin(), src.begin() + victim.size(), src.get_allocator()), "failed to properly copy of source ?" , test_name ); |
1124 | }else{ |
1125 | ASSERT_IN_TEST( std::equal(victim.begin(), victim.begin() + planned_victim_size, src.begin()), "failed to properly copy items before the exception?" , test_name ); |
1126 | ASSERT_IN_TEST( ::all_of( victim.begin() + planned_victim_size, victim.end(), is_state_f<Foo::ZeroInitialized>() ), "failed to zero-initialize items left not constructed after the exception?" , test_name ); |
1127 | } |
1128 | } |
1129 | |
1130 | //------------------------------------------------------------------------ |
1131 | // Test exceptions safety (from allocator and items constructors) |
1132 | //------------------------------------------------------------------------ |
1133 | void TestExceptions() { |
1134 | typedef static_counting_allocator<debug_allocator<FooWithAssign>, std::size_t> allocator_t; |
1135 | typedef tbb::concurrent_vector<FooWithAssign, allocator_t> vector_t; |
1136 | |
1137 | enum methods { |
1138 | zero_method = 0, |
1139 | ctor_copy, ctor_size, assign_nt, assign_ir, reserve, compact, |
1140 | all_methods |
1141 | }; |
1142 | ASSERT( !FooCount, NULL ); |
1143 | |
1144 | try { |
1145 | vector_t src(FooIterator(0), FooIterator(N)); // original data |
1146 | |
1147 | for(int t = 0; t < 2; ++t) // exception type |
1148 | for(int m = zero_method+1; m < all_methods; ++m) |
1149 | { |
1150 | track_foo_count<__LINE__> check_all_foo_destroyed_on_exit("TestExceptions" ); |
1151 | track_allocator_memory<allocator_t> verify_no_leak_at_exit("TestExceptions" ); |
1152 | allocator_t::init_counters(); |
1153 | if(t) MaxFooCount = FooCount + N/4; |
1154 | else allocator_t::set_limits(N/4); |
1155 | vector_t victim; |
1156 | try { |
1157 | switch(m) { |
1158 | case ctor_copy: { |
1159 | vector_t acopy(src); |
1160 | } break; // auto destruction after exception is checked by ~Foo |
1161 | case ctor_size: { |
1162 | vector_t sized(N); |
1163 | } break; // auto destruction after exception is checked by ~Foo |
1164 | // Do not test assignment constructor due to reusing of same methods as below |
1165 | case assign_nt: { |
1166 | victim.assign(N, FooWithAssign()); |
1167 | } break; |
1168 | case assign_ir: { |
1169 | victim.assign(FooIterator(0), FooIterator(N)); |
1170 | } break; |
1171 | case reserve: { |
1172 | try { |
1173 | victim.reserve(victim.max_size()+1); |
1174 | } catch(std::length_error &) { |
1175 | } catch(...) { |
1176 | KNOWN_ISSUE("ERROR: unrecognized exception - known compiler issue\n" ); |
1177 | } |
1178 | victim.reserve(N); |
1179 | } break; |
1180 | case compact: { |
1181 | if(t) MaxFooCount = 0; else allocator_t::set_limits(); // reset limits |
1182 | victim.reserve(2); victim = src; // fragmented assignment |
1183 | if(t) MaxFooCount = FooCount + 10; else allocator_t::set_limits(1, false); // block any allocation, check NULL return from allocator |
1184 | victim.shrink_to_fit(); // should start defragmenting first segment |
1185 | } break; |
1186 | default:; |
1187 | } |
1188 | if(!t || m != reserve) ASSERT(false, "should throw an exception" ); |
1189 | } catch(std::bad_alloc &e) { |
1190 | allocator_t::set_limits(); MaxFooCount = 0; |
1191 | size_t capacity = victim.capacity(); |
1192 | size_t size = victim.size(); |
1193 | |
1194 | size_t req_size = get_early_size(victim); |
1195 | |
1196 | verify_c_vector_size(size, capacity, req_size, "TestExceptions" ); |
1197 | |
1198 | switch(m) { |
1199 | case reserve: |
1200 | if(t) ASSERT(false, NULL); |
1201 | __TBB_fallthrough; |
1202 | case assign_nt: |
1203 | case assign_ir: |
1204 | if(!t) { |
1205 | ASSERT(capacity < N/2, "unexpected capacity" ); |
1206 | ASSERT(size == 0, "unexpected size" ); |
1207 | break; |
1208 | } else { |
1209 | ASSERT(size == N, "unexpected size" ); |
1210 | ASSERT(capacity >= N, "unexpected capacity" ); |
1211 | int i; |
1212 | for(i = 1; ; ++i) |
1213 | if(!victim[i].zero_bar()) break; |
1214 | else ASSERT(victim[i].bar() == (m == assign_ir? i : initial_value_of_bar), NULL); |
1215 | for(; size_t(i) < size; ++i) ASSERT(!victim[i].zero_bar(), NULL); |
1216 | ASSERT(size_t(i) == size, NULL); |
1217 | break; |
1218 | } |
1219 | case compact: |
1220 | ASSERT(capacity > 0, "unexpected capacity" ); |
1221 | ASSERT(victim == src, "shrink_to_fit() is broken" ); |
1222 | break; |
1223 | |
1224 | default:; // nothing to check here |
1225 | } |
1226 | REMARK("Exception %d: %s\t- ok\n" , m, e.what()); |
1227 | } |
1228 | } |
1229 | } catch(...) { |
1230 | ASSERT(false, "unexpected exception" ); |
1231 | } |
1232 | } |
1233 | |
1234 | //TODO: split into two separate tests |
1235 | //TODO: remove code duplication in exception safety tests |
1236 | void TestExceptionSafetyGuaranteesForAssignOperator(){ |
1237 | //TODO: use __FUNCTION__ for test name |
1238 | const char* const test_name = "TestExceptionSafetyGuaranteesForAssignOperator" ; |
1239 | typedef static_counting_allocator<debug_allocator<FooWithAssign>, std::size_t> allocator_t; |
1240 | typedef tbb::concurrent_vector<FooWithAssign, allocator_t> vector_t; |
1241 | |
1242 | track_foo_count<__LINE__> check_all_foo_destroyed_on_exit(test_name); |
1243 | track_allocator_memory<allocator_t> verify_no_leak_at_exit(test_name); |
1244 | |
1245 | vector_t src(FooIterator(0), FooIterator(N)); // original data |
1246 | |
1247 | const size_t planned_victim_size = N/4; |
1248 | |
1249 | for(int t = 0; t < 2; ++t) {// exception type |
1250 | vector_t victim; |
1251 | victim.reserve(2); // get fragmented assignment |
1252 | |
1253 | ASSERT_THROWS_IN_TEST( |
1254 | { |
1255 | limit_foo_count_in_scope foo_limit(FooCount + planned_victim_size, t); |
1256 | limit_allocated_items_in_scope<allocator_t> allocator_limit(allocator_t::items_allocated + planned_victim_size, !t); |
1257 | |
1258 | victim = src; // fragmented assignment |
1259 | }, |
1260 | std::bad_alloc, "" , test_name |
1261 | ); |
1262 | |
1263 | verify_c_vector_size(victim, test_name); |
1264 | |
1265 | if(!t) { |
1266 | verify_c_vector_capacity_is_below(victim.capacity(), N, test_name); |
1267 | } |
1268 | |
1269 | verify_vector_partially_copied(victim, planned_victim_size, src, !t, test_name); |
1270 | verify_last_segment_allocation_failed(victim, test_name); |
1271 | verify_copy_and_assign_from_produce_the_same(victim, test_name); |
1272 | verify_assignment_operator_throws_bad_last_alloc(victim, test_name); |
1273 | } |
1274 | } |
1275 | //TODO: split into two separate tests |
1276 | void TestExceptionSafetyGuaranteesForConcurrentGrow(){ |
1277 | const char* const test_name = "TestExceptionSafetyGuaranteesForConcurrentGrow" ; |
1278 | typedef static_counting_allocator<debug_allocator<FooWithAssign>, std::size_t> allocator_t; |
1279 | typedef tbb::concurrent_vector<FooWithAssign, allocator_t> vector_t; |
1280 | |
1281 | track_foo_count<__LINE__> check_all_foo_destroyed_on_exit(test_name); |
1282 | track_allocator_memory<allocator_t> verify_no_leak_at_exit(test_name); |
1283 | |
1284 | vector_t src(FooIterator(0), FooIterator(N)); // original data |
1285 | |
1286 | const size_t planned_victim_size = N/4; |
1287 | static const int grain_size = 70; |
1288 | |
1289 | tbb::task_scheduler_init init(2); |
1290 | |
1291 | for(int t = 0; t < 2; ++t) {// exception type |
1292 | vector_t victim; |
1293 | |
1294 | #if TBB_USE_CAPTURED_EXCEPTION |
1295 | #define EXPECTED_EXCEPTION tbb::captured_exception |
1296 | #else |
1297 | #define EXPECTED_EXCEPTION std::bad_alloc |
1298 | #endif |
1299 | |
1300 | ASSERT_THROWS_IN_TEST( |
1301 | { |
1302 | limit_foo_count_in_scope foo_limit(FooCount + 31, t); // these numbers help to reproduce the live lock for versions < TBB2.2 |
1303 | limit_allocated_items_in_scope<allocator_t> allocator_limit(allocator_t::items_allocated + planned_victim_size, !t); |
1304 | |
1305 | grain_map m(concurrent_grow_single_range_map, Harness::end(concurrent_grow_single_range_map)); |
1306 | |
1307 | static const size_t part_weight = grain_size / m.total_number_of_parts; |
1308 | |
1309 | tbb::parallel_for( |
1310 | tbb::blocked_range<size_t>(0, N, grain_size), |
1311 | GrowBy<vector_t>(victim, m, part_weight) |
1312 | ); |
1313 | }, |
1314 | EXPECTED_EXCEPTION, "" , test_name |
1315 | ); |
1316 | |
1317 | verify_c_vector_size(victim, test_name); |
1318 | |
1319 | if(!t) { |
1320 | verify_c_vector_capacity_is_below(victim.capacity(), N, test_name); |
1321 | } |
1322 | |
1323 | for(int i = 0; ; ++i) { |
1324 | try { |
1325 | Foo &foo = victim.at(i); |
1326 | ASSERT( foo.is_valid_or_zero(),"" ); |
1327 | } catch(std::range_error &) { // skip broken segment |
1328 | ASSERT( size_t(i) < get_early_size(victim), NULL ); |
1329 | } catch(std::out_of_range &){ |
1330 | ASSERT( i > 0, NULL ); break; |
1331 | } catch(...) { |
1332 | KNOWN_ISSUE("ERROR: unrecognized exception - known compiler issue\n" ); break; |
1333 | } |
1334 | } |
1335 | |
1336 | verify_copy_and_assign_from_produce_the_same(victim, test_name); |
1337 | } |
1338 | } |
1339 | |
1340 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
1341 | void TestExceptionSafetyGuaranteesForMoveAssignOperatorWithUnEqualAllocatorMemoryFailure(){ |
1342 | const char* const test_name = "TestExceptionSafetyGuaranteesForMoveAssignOperatorWithUnEqualAllocatorMemoryFailure" ; |
1343 | |
1344 | //TODO: add ability to inject debug_allocator into stateful_allocator_fixture::allocator_t |
1345 | //typedef static_counting_allocator<debug_allocator<FooWithAssign>, std::size_t> allocator_t; |
1346 | typedef default_stateful_fixture_make_helper<c_vector_type, Harness::false_type>::type fixture_t; |
1347 | typedef arena_allocator_fixture<FooWithAssign, Harness::false_type> arena_allocator_fixture_t; |
1348 | typedef fixture_t::allocator_t allocator_t; |
1349 | typedef fixture_t::container_t vector_t; |
1350 | |
1351 | fixture_t fixture(test_name); |
1352 | arena_allocator_fixture_t arena_allocator_fixture(4 * fixture.container_size); |
1353 | |
1354 | const size_t allocation_limit = fixture.container_size/4; |
1355 | |
1356 | vector_t victim(arena_allocator_fixture.allocator); |
1357 | victim.reserve(2); // get fragmented assignment |
1358 | |
1359 | ASSERT_THROWS_IN_TEST( |
1360 | { |
1361 | limit_allocated_items_in_scope<allocator_t> allocator_limit(allocator_t::items_allocated + allocation_limit); |
1362 | victim = std::move(fixture.source); // fragmented assignment |
1363 | }, |
1364 | std::bad_alloc, "" , test_name |
1365 | ); |
1366 | |
1367 | verify_c_vector_size(victim, test_name); |
1368 | verify_c_vector_capacity_is_below(victim.capacity(), allocation_limit + 2, test_name); |
1369 | |
1370 | fixture.verify_part_of_content_deep_moved(victim, victim.size()); |
1371 | |
1372 | verify_last_segment_allocation_failed(victim, test_name); |
1373 | verify_copy_and_assign_from_produce_the_same(victim, test_name); |
1374 | verify_assignment_operator_throws_bad_last_alloc(victim, test_name); |
1375 | } |
1376 | |
1377 | void TestExceptionSafetyGuaranteesForMoveAssignOperatorWithUnEqualAllocatorExceptionInElementCtor(){ |
1378 | const char* const test_name = "TestExceptionSafetyGuaranteesForMoveAssignOperator" ; |
1379 | //typedef static_counting_allocator<debug_allocator<FooWithAssign>, std::size_t> allocator_t; |
1380 | typedef default_stateful_fixture_make_helper<c_vector_type, Harness::false_type>::type fixture_t; |
1381 | typedef arena_allocator_fixture<FooWithAssign, Harness::false_type> arena_allocator_fixture_t; |
1382 | typedef fixture_t::container_t vector_t; |
1383 | |
1384 | fixture_t fixture(test_name); |
1385 | const size_t planned_victim_size = fixture.container_size/4; |
1386 | arena_allocator_fixture_t arena_allocator_fixture(4 * fixture.container_size); |
1387 | |
1388 | vector_t victim(arena_allocator_fixture.allocator); |
1389 | victim.reserve(2); // get fragmented assignment |
1390 | |
1391 | ASSERT_THROWS_IN_TEST( |
1392 | { |
1393 | limit_foo_count_in_scope foo_limit(FooCount + planned_victim_size); |
1394 | victim = std::move(fixture.source); // fragmented assignment |
1395 | }, |
1396 | std::bad_alloc, "" , test_name |
1397 | ); |
1398 | |
1399 | verify_c_vector_size(victim, test_name); |
1400 | |
1401 | fixture.verify_part_of_content_deep_moved(victim, planned_victim_size); |
1402 | |
1403 | verify_last_segment_allocation_failed(victim, test_name); |
1404 | verify_copy_and_assign_from_produce_the_same(victim, test_name); |
1405 | verify_assignment_operator_throws_bad_last_alloc(victim, test_name); |
1406 | } |
1407 | #endif //__TBB_CPP11_RVALUE_REF_PRESENT |
1408 | |
1409 | namespace push_back_exception_safety_helpers{ |
1410 | //TODO: remove code duplication with emplace_helpers::wrapper_type |
1411 | struct throwing_foo:Foo{ |
1412 | int value1; |
1413 | int value2; |
1414 | explicit throwing_foo(int v1, int v2) : value1 (v1), value2(v2) { } |
1415 | }; |
1416 | |
1417 | template< typename foo_t = throwing_foo> |
1418 | struct fixture{ |
1419 | typedef tbb::concurrent_vector<foo_t, debug_allocator<foo_t> > vector_t; |
1420 | vector_t v; |
1421 | |
1422 | void test( void(*p_test)(vector_t&), const char * test_name){ |
1423 | track_foo_count<__LINE__> verify_no_foo_leaked_during_exception(test_name); |
1424 | ASSERT_IN_TEST(v.empty(),"incorrect test setup?" , test_name ); |
1425 | ASSERT_THROWS_IN_TEST(p_test(v), Foo_exception ,"" , test_name); |
1426 | ASSERT_IN_TEST(is_state<Foo::ZeroInitialized>(v[0]),"incorrectly filled item during exception in emplace_back?" , test_name); |
1427 | } |
1428 | }; |
1429 | } |
1430 | |
1431 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
1432 | void TestPushBackMoveExceptionSafety(){ |
1433 | typedef push_back_exception_safety_helpers::fixture<Foo> fixture_t; |
1434 | fixture_t t; |
1435 | |
1436 | limit_foo_count_in_scope foo_limit(FooCount + 1); |
1437 | |
1438 | struct test{ |
1439 | static void test_move_push_back(fixture_t::vector_t& v){ |
1440 | Foo f; |
1441 | v.push_back(std::move(f)); |
1442 | } |
1443 | }; |
1444 | t.test(&test::test_move_push_back, "TestPushBackMoveExceptionSafety" ); |
1445 | } |
1446 | |
1447 | #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
1448 | void TestEmplaceBackExceptionSafety(){ |
1449 | typedef push_back_exception_safety_helpers::fixture<> fixture_t; |
1450 | fixture_t t; |
1451 | |
1452 | Foo dummy; //make FooCount non zero; |
1453 | Harness::suppress_unused_warning(dummy); |
1454 | limit_foo_count_in_scope foo_limit(FooCount); |
1455 | |
1456 | struct test{ |
1457 | static void test_emplace(fixture_t::vector_t& v){ |
1458 | v.emplace_back(1,2); |
1459 | } |
1460 | }; |
1461 | t.test(&test::test_emplace, "TestEmplaceBackExceptionSafety" ); |
1462 | } |
1463 | #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
1464 | #endif //__TBB_CPP11_RVALUE_REF_PRESENT |
1465 | |
1466 | #endif /* TBB_USE_EXCEPTIONS */ |
1467 | |
1468 | //------------------------------------------------------------------------ |
1469 | // Test support for SIMD instructions |
1470 | //------------------------------------------------------------------------ |
1471 | #include "harness_m128.h" |
1472 | |
1473 | #if HAVE_m128 || HAVE_m256 |
1474 | |
1475 | template<typename ClassWithVectorType> |
1476 | void TestVectorTypes() { |
1477 | tbb::concurrent_vector<ClassWithVectorType> v; |
1478 | for( int i=0; i<100; ++i ) { |
1479 | // VC8 does not properly align a temporary value; to work around, use explicit variable |
1480 | ClassWithVectorType foo(i); |
1481 | v.push_back(foo); |
1482 | for( int j=0; j<i; ++j ) { |
1483 | ClassWithVectorType bar(j); |
1484 | ASSERT( v[j]==bar, NULL ); |
1485 | } |
1486 | } |
1487 | } |
1488 | #endif /* HAVE_m128 | HAVE_m256 */ |
1489 | |
1490 | //------------------------------------------------------------------------ |
1491 | |
1492 | namespace v3_backward_compatibility{ |
1493 | namespace segment_t_layout_helpers{ |
1494 | //this is previous definition of according inner class of concurrent_vector_base_v3 |
1495 | struct segment_t_v3 { |
1496 | void* array; |
1497 | }; |
1498 | //helper class to access protected members of concurrent_vector_base |
1499 | struct access_vector_fields :tbb::internal::concurrent_vector_base_v3 { |
1500 | using tbb::internal::concurrent_vector_base_v3::segment_t; |
1501 | using tbb::internal::concurrent_vector_base_v3::segment_index_t; |
1502 | using tbb::internal::concurrent_vector_base_v3::pointers_per_long_table; |
1503 | using tbb::internal::concurrent_vector_base_v3::internal_segments_table; |
1504 | }; |
1505 | //this is previous definition of according inner class of concurrent_vector_base_v3 |
1506 | struct internal_segments_table_v3 { |
1507 | access_vector_fields::segment_index_t first_block; |
1508 | segment_t_v3 table[access_vector_fields::pointers_per_long_table]; |
1509 | }; |
1510 | |
1511 | template <typename checked_type> |
1512 | struct alignment_check_helper{ |
1513 | char dummy; |
1514 | checked_type checked; |
1515 | }; |
1516 | } |
1517 | void TestSegmentTLayout(){ |
1518 | using namespace segment_t_layout_helpers; |
1519 | typedef alignment_check_helper<segment_t_v3> structure_with_old_segment_type; |
1520 | typedef alignment_check_helper<access_vector_fields::segment_t> structure_with_new_segment_type; |
1521 | |
1522 | ASSERT((sizeof(structure_with_old_segment_type)==sizeof(structure_with_new_segment_type)) |
1523 | ,"layout of new segment_t and old one differ?" ); |
1524 | } |
1525 | |
1526 | void TestInternalSegmentsTableLayout(){ |
1527 | using namespace segment_t_layout_helpers; |
1528 | typedef alignment_check_helper<internal_segments_table_v3> structure_with_old_segment_table_type; |
1529 | typedef alignment_check_helper<access_vector_fields::internal_segments_table> structure_with_new_segment_table_type; |
1530 | |
1531 | ASSERT((sizeof(structure_with_old_segment_table_type)==sizeof(structure_with_new_segment_table_type)) |
1532 | ,"layout of new internal_segments_table and old one differ?" ); |
1533 | } |
1534 | } |
1535 | void TestV3BackwardCompatibility(){ |
1536 | using namespace v3_backward_compatibility; |
1537 | TestSegmentTLayout(); |
1538 | TestInternalSegmentsTableLayout(); |
1539 | } |
1540 | |
1541 | #include "harness_defs.h" |
1542 | |
1543 | #include <vector> |
1544 | #include <numeric> |
1545 | #include <functional> |
1546 | |
1547 | // The helper to run a test only when a default construction is present. |
1548 | template <bool default_construction_present> struct do_default_construction_test { |
1549 | template<typename FuncType> void operator() ( FuncType func ) const { func(); } |
1550 | }; |
1551 | template <> struct do_default_construction_test<false> { |
1552 | template<typename FuncType> void operator()( FuncType ) const {} |
1553 | }; |
1554 | |
1555 | template <typename Type, typename Allocator> |
1556 | class test_grow_by_and_resize : NoAssign { |
1557 | tbb::concurrent_vector<Type, Allocator> &my_c; |
1558 | public: |
1559 | test_grow_by_and_resize( tbb::concurrent_vector<Type, Allocator> &c ) : my_c(c) {} |
1560 | void operator()() const { |
1561 | const typename tbb::concurrent_vector<Type, Allocator>::size_type sz = my_c.size(); |
1562 | my_c.grow_by( 5 ); |
1563 | ASSERT( my_c.size() == sz + 5, NULL ); |
1564 | my_c.resize( sz ); |
1565 | ASSERT( my_c.size() == sz, NULL ); |
1566 | } |
1567 | }; |
1568 | |
1569 | template <typename Type, typename Allocator> |
1570 | void CompareVectors( const tbb::concurrent_vector<Type, Allocator> &c1, const tbb::concurrent_vector<Type, Allocator> &c2 ) { |
1571 | ASSERT( !(c1 == c2) && c1 != c2, NULL ); |
1572 | ASSERT( c1 <= c2 && c1 < c2 && c2 >= c1 && c2 > c1, NULL ); |
1573 | } |
1574 | |
1575 | #if __TBB_CPP11_SMART_POINTERS_PRESENT |
1576 | template <typename Type, typename Allocator> |
1577 | void CompareVectors( const tbb::concurrent_vector<std::weak_ptr<Type>, Allocator> &, const tbb::concurrent_vector<std::weak_ptr<Type>, Allocator> & ) { |
1578 | /* do nothing for std::weak_ptr */ |
1579 | } |
1580 | #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */ |
1581 | |
1582 | template <bool default_construction_present, typename Type, typename Allocator> |
1583 | void Examine( tbb::concurrent_vector<Type, Allocator> c, const std::vector<Type> &vec ) { |
1584 | typedef tbb::concurrent_vector<Type, Allocator> vector_t; |
1585 | typedef typename vector_t::size_type size_type_t; |
1586 | |
1587 | ASSERT( c.size() == vec.size(), NULL ); |
1588 | for ( size_type_t i=0; i<c.size(); ++i ) ASSERT( Harness::IsEqual()(c[i], vec[i]), NULL ); |
1589 | do_default_construction_test<default_construction_present>()(test_grow_by_and_resize<Type,Allocator>(c)); |
1590 | c.grow_by( size_type_t(5), c[0] ); |
1591 | c.grow_to_at_least( c.size()+5, c.at(0) ); |
1592 | vector_t c2; |
1593 | c2.reserve( 5 ); |
1594 | std::copy( c.begin(), c.begin() + 5, std::back_inserter( c2 ) ); |
1595 | |
1596 | c.grow_by( c2.begin(), c2.end() ); |
1597 | const vector_t& cvcr = c; |
1598 | ASSERT( Harness::IsEqual()(cvcr.front(), *(c2.rend()-1)), NULL ); |
1599 | ASSERT( Harness::IsEqual()(cvcr.back(), *c2.rbegin()), NULL); |
1600 | ASSERT( Harness::IsEqual()(*c.cbegin(), *(c.crend()-1)), NULL ); |
1601 | ASSERT( Harness::IsEqual()(*(c.cend()-1), *c.crbegin()), NULL ); |
1602 | c.swap( c2 ); |
1603 | ASSERT( c.size() == 5, NULL ); |
1604 | CompareVectors( c, c2 ); |
1605 | c.swap( c2 ); |
1606 | c2.clear(); |
1607 | ASSERT( c2.size() == 0, NULL ); |
1608 | c2.shrink_to_fit(); |
1609 | Allocator a = c.get_allocator(); |
1610 | a.deallocate( a.allocate(1), 1 ); |
1611 | } |
1612 | |
1613 | template <typename Type> |
1614 | class test_default_construction : NoAssign { |
1615 | const std::vector<Type> &my_vec; |
1616 | public: |
1617 | test_default_construction( const std::vector<Type> &vec ) : my_vec(vec) {} |
1618 | void operator()() const { |
1619 | // Construction with initial size specified by argument n. |
1620 | tbb::concurrent_vector<Type> c7( my_vec.size() ); |
1621 | std::copy( my_vec.begin(), my_vec.end(), c7.begin() ); |
1622 | Examine</*default_construction_present = */true>( c7, my_vec ); |
1623 | tbb::concurrent_vector< Type, debug_allocator<Type> > c8( my_vec.size() ); |
1624 | std::copy( c7.begin(), c7.end(), c8.begin() ); |
1625 | Examine</*default_construction_present = */true>( c8, my_vec ); |
1626 | } |
1627 | }; |
1628 | |
1629 | template <bool default_construction_present, typename Type> |
1630 | void TypeTester( const std::vector<Type> &vec ) { |
1631 | __TBB_ASSERT( vec.size() >= 5, "Array should have at least 5 elements" ); |
1632 | // Construct empty vector. |
1633 | tbb::concurrent_vector<Type> c1; |
1634 | std::copy( vec.begin(), vec.end(), std::back_inserter(c1) ); |
1635 | Examine<default_construction_present>( c1, vec ); |
1636 | #if __TBB_INITIALIZER_LISTS_PRESENT |
1637 | // Constructor from initializer_list. |
1638 | tbb::concurrent_vector<Type> c2({vec[0],vec[1],vec[2]}); |
1639 | std::copy( vec.begin()+3, vec.end(), std::back_inserter(c2) ); |
1640 | Examine<default_construction_present>( c2, vec ); |
1641 | #endif |
1642 | // Copying constructor. |
1643 | tbb::concurrent_vector<Type> c3(c1); |
1644 | Examine<default_construction_present>( c3, vec ); |
1645 | // Construct with non-default allocator |
1646 | tbb::concurrent_vector< Type, debug_allocator<Type> > c4; |
1647 | std::copy( vec.begin(), vec.end(), std::back_inserter(c4) ); |
1648 | Examine<default_construction_present>( c4, vec ); |
1649 | // Copying constructor for vector with different allocator type. |
1650 | tbb::concurrent_vector<Type> c5(c4); |
1651 | Examine<default_construction_present>( c5, vec ); |
1652 | tbb::concurrent_vector< Type, debug_allocator<Type> > c6(c3); |
1653 | Examine<default_construction_present>( c6, vec ); |
1654 | // Construction with initial size specified by argument n. |
1655 | do_default_construction_test<default_construction_present>()(test_default_construction<Type>(vec)); |
1656 | // Construction with initial size specified by argument n, initialization by copying of t, and given allocator instance. |
1657 | debug_allocator<Type> allocator; |
1658 | tbb::concurrent_vector< Type, debug_allocator<Type> > c9(vec.size(), vec[1], allocator); |
1659 | Examine<default_construction_present>( c9, std::vector<Type>(vec.size(), vec[1]) ); |
1660 | // Construction with copying iteration range and given allocator instance. |
1661 | tbb::concurrent_vector< Type, debug_allocator<Type> > c10(c1.begin(), c1.end(), allocator); |
1662 | Examine<default_construction_present>( c10, vec ); |
1663 | tbb::concurrent_vector<Type> c11(vec.begin(), vec.end()); |
1664 | Examine<default_construction_present>( c11, vec ); |
1665 | } |
1666 | |
1667 | void TestTypes() { |
1668 | const int NUMBER = 100; |
1669 | |
1670 | std::vector<int> intArr; |
1671 | for ( int i=0; i<NUMBER; ++i ) intArr.push_back(i); |
1672 | TypeTester</*default_construction_present = */true>( intArr ); |
1673 | |
1674 | #if __TBB_CPP11_REFERENCE_WRAPPER_PRESENT && !__TBB_REFERENCE_WRAPPER_COMPILATION_BROKEN |
1675 | std::vector< std::reference_wrapper<int> > refArr; |
1676 | // The constructor of std::reference_wrapper<T> from T& is explicit in some versions of libstdc++. |
1677 | for ( int i=0; i<NUMBER; ++i ) refArr.push_back( std::reference_wrapper<int>(intArr[i]) ); |
1678 | TypeTester</*default_construction_present = */false>( refArr ); |
1679 | #else |
1680 | REPORT( "Known issue: C++11 reference wrapper tests are skipped.\n" ); |
1681 | #endif /* __TBB_CPP11_REFERENCE_WRAPPER_PRESENT && !__TBB_REFERENCE_WRAPPER_COMPILATION_BROKEN */ |
1682 | |
1683 | std::vector< tbb::atomic<int> > tbbIntArr( NUMBER ); |
1684 | for ( int i=0; i<NUMBER; ++i ) tbbIntArr[i] = i; |
1685 | TypeTester</*default_construction_present = */true>( tbbIntArr ); |
1686 | |
1687 | #if __TBB_CPP11_SMART_POINTERS_PRESENT |
1688 | std::vector< std::shared_ptr<int> > shrPtrArr; |
1689 | for ( int i=0; i<NUMBER; ++i ) shrPtrArr.push_back( std::make_shared<int>(i) ); |
1690 | TypeTester</*default_construction_present = */true>( shrPtrArr ); |
1691 | |
1692 | std::vector< std::weak_ptr<int> > wkPtrArr; |
1693 | std::copy( shrPtrArr.begin(), shrPtrArr.end(), std::back_inserter(wkPtrArr) ); |
1694 | TypeTester</*default_construction_present = */true>( wkPtrArr ); |
1695 | #else |
1696 | REPORT( "Known issue: C++11 smart pointer tests are skipped.\n" ); |
1697 | #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */ |
1698 | } |
1699 | |
1700 | #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT |
1701 | template <template <typename...> typename TVector> |
1702 | void TestDeductionGuides() { |
1703 | using ComplexType = const std::string*; |
1704 | std::vector<ComplexType> v; |
1705 | std::string s = "s" ; |
1706 | auto l = {ComplexType(&s), ComplexType(&s)}; |
1707 | |
1708 | // check TVector(InputIterator, InputIterator) |
1709 | TVector v1(v.begin(), v.end()); |
1710 | static_assert(std::is_same<decltype(v1), TVector<ComplexType>>::value); |
1711 | |
1712 | // check TVector(InputIterator, InputIterator, Alocator) |
1713 | TVector v2(v.begin(), v.end(), std::allocator<ComplexType>()); |
1714 | static_assert(std::is_same<decltype(v2), |
1715 | TVector<ComplexType, std::allocator<ComplexType>>>::value); |
1716 | |
1717 | // check TVector(std::initializer_list<T>) |
1718 | TVector v3(l); |
1719 | static_assert(std::is_same<decltype(v3), |
1720 | TVector<ComplexType>>::value); |
1721 | |
1722 | // check TVector(std::initializer_list, Alocator) |
1723 | TVector v4(l, std::allocator<ComplexType>()); |
1724 | static_assert(std::is_same<decltype(v4), TVector<ComplexType, std::allocator<ComplexType>>>::value); |
1725 | |
1726 | // check TVector(TVector&) |
1727 | TVector v5(v1); |
1728 | static_assert(std::is_same<decltype(v5), TVector<ComplexType>>::value); |
1729 | |
1730 | // check TVector(TVector&, Allocator) |
1731 | TVector v6(v5, std::allocator<ComplexType>()); |
1732 | static_assert(std::is_same<decltype(v6), TVector<ComplexType, std::allocator<ComplexType>>>::value); |
1733 | |
1734 | // check TVector(TVector&&) |
1735 | TVector v7(std::move(v1)); |
1736 | static_assert(std::is_same<decltype(v7), decltype(v1)>::value); |
1737 | |
1738 | // check TVector(TVector&&, Allocator) |
1739 | TVector v8(std::move(v5), std::allocator<ComplexType>()); |
1740 | static_assert(std::is_same<decltype(v8), TVector<ComplexType, std::allocator<ComplexType>>>::value); |
1741 | |
1742 | // check TVector(TVector&, Allocator) |
1743 | TVector v9(v1, std::allocator<ComplexType>()); |
1744 | static_assert(std::is_same<decltype(v9), TVector<ComplexType, std::allocator<ComplexType>>>::value); |
1745 | |
1746 | } |
1747 | #endif |
1748 | |
1749 | // Currently testing compilation issues with polymorphic allocator, but concurrent_vector does not |
1750 | // provide full support yet. |
1751 | // TODO: extend test with full checking polymorphic_allocator with concurrent_vector |
1752 | void TestPMRSupport() { |
1753 | typedef pmr_stateful_allocator<int> AType; |
1754 | typedef tbb::concurrent_vector<int, AType> VType; |
1755 | const int VEC_SIZE = 1000; |
1756 | |
1757 | AType original_alloc; |
1758 | VType c(original_alloc); |
1759 | |
1760 | // General compilation test |
1761 | for( int i = 0; i < VEC_SIZE; ++i ) { |
1762 | c.push_back(i*i); |
1763 | } |
1764 | VType::const_iterator p = c.begin(); |
1765 | for( int i = 0; i < VEC_SIZE; ++i ) { |
1766 | ASSERT( *p == i*i, NULL ); ++p; |
1767 | } |
1768 | |
1769 | // Check that swap is allocator aware |
1770 | AType swap_alloc; |
1771 | VType swap_container(swap_alloc); swap_container.swap(c); |
1772 | ASSERT(c.get_allocator() != swap_container.get_allocator(), "Allocator was swapped, it shouldn't" ); |
1773 | |
1774 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
1775 | // Move assignment operator deleted, container is allocator aware |
1776 | AType move_alloc; |
1777 | VType move_container(move_alloc); |
1778 | move_container = std::move(c); |
1779 | ASSERT(c.get_allocator() != move_container.get_allocator(), "Allocator was moved, it shouldn't" ); |
1780 | #endif |
1781 | } |
1782 | |
1783 | int TestMain () { |
1784 | if( MinThread<1 ) { |
1785 | REPORT("ERROR: MinThread=%d, but must be at least 1\n" ,MinThread); MinThread = 1; |
1786 | } |
1787 | TestFoo(); |
1788 | TestV3BackwardCompatibility(); |
1789 | TestIteratorTraits<tbb::concurrent_vector<Foo>::iterator,Foo>(); |
1790 | TestIteratorTraits<tbb::concurrent_vector<Foo>::const_iterator,const Foo>(); |
1791 | TestArrayLength(); |
1792 | TestAllOf(); |
1793 | #if __TBB_INITIALIZER_LISTS_PRESENT |
1794 | TestInitList(); |
1795 | #else |
1796 | REPORT("Known issue: initializer list tests are skipped.\n" ); |
1797 | #endif |
1798 | TestSequentialFor<FooWithAssign> (); |
1799 | TestResizeAndCopy(); |
1800 | TestAssign(); |
1801 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
1802 | TestMoveConstructor<c_vector_type>(); |
1803 | TestMoveAssignOperator<c_vector_type>(); |
1804 | TestConstructorWithMoveIterators<c_vector_type>(); |
1805 | TestAssignWithMoveIterators<c_vector_type>(); |
1806 | TestSerialGrowByWithMoveIterators(); |
1807 | #if __TBB_MOVE_IF_NOEXCEPT_PRESENT |
1808 | TestSerialMoveInShrinkToFit(); |
1809 | #endif // __TBB_MOVE_IF_NOEXCEPT_PRESENT |
1810 | #else |
1811 | REPORT("Known issue: tests for vector move constructor/assignment operator are skipped.\n" ); |
1812 | #endif |
1813 | TestGrowToAtLeastWithSourceParameter<tbb::concurrent_vector<int> >(12345); |
1814 | TestSerialGrowByRange(false); |
1815 | TestSerialGrowByRange(true); |
1816 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
1817 | TestPushBackMoveOnlyContainee(); |
1818 | #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
1819 | TestEmplaceBack(); |
1820 | #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
1821 | #endif //__TBB_CPP11_RVALUE_REF_PRESENT |
1822 | #if HAVE_m128 |
1823 | TestVectorTypes<ClassWithSSE>(); |
1824 | #endif |
1825 | #if HAVE_m256 |
1826 | if (have_AVX()) TestVectorTypes<ClassWithAVX>(); |
1827 | #endif |
1828 | TestCapacity(); |
1829 | ASSERT( !FooCount, NULL ); |
1830 | for( int nthread=MinThread; nthread<=MaxThread; ++nthread ) { |
1831 | tbb::task_scheduler_init init( nthread ); |
1832 | TestParallelFor( nthread ); |
1833 | TestConcurrentGrowToAtLeast(); |
1834 | TestConcurrentGrowBy( nthread ); |
1835 | } |
1836 | ASSERT( !FooCount, NULL ); |
1837 | TestComparison(); |
1838 | TestFindPrimes(); |
1839 | TestSort(); |
1840 | #if __TBB_RANGE_BASED_FOR_PRESENT |
1841 | TestRangeBasedFor(); |
1842 | #endif //if __TBB_RANGE_BASED_FOR_PRESENT |
1843 | #if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN |
1844 | REPORT("Known issue: exception safety test is skipped.\n" ); |
1845 | #elif TBB_USE_EXCEPTIONS |
1846 | TestExceptions(); |
1847 | TestExceptionSafetyGuaranteesForAssignOperator(); |
1848 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
1849 | TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorMemoryFailure<c_vector_type>(); |
1850 | TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorExceptionInElementCtor<c_vector_type>(); |
1851 | TestExceptionSafetyGuaranteesForMoveAssignOperatorWithUnEqualAllocatorMemoryFailure(); |
1852 | TestExceptionSafetyGuaranteesForMoveAssignOperatorWithUnEqualAllocatorExceptionInElementCtor(); |
1853 | TestPushBackMoveExceptionSafety(); |
1854 | #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT |
1855 | TestEmplaceBackExceptionSafety(); |
1856 | #endif /*__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT */ |
1857 | #else |
1858 | REPORT("Known issue: exception safety tests for move constructor/assignment operator , grow_by are skipped.\n" ); |
1859 | #endif /*__TBB_CPP11_RVALUE_REF_PRESENT */ |
1860 | #endif /* TBB_USE_EXCEPTIONS */ |
1861 | TestTypes(); |
1862 | #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT |
1863 | TestDeductionGuides<tbb::concurrent_vector>(); |
1864 | #endif |
1865 | TestPMRSupport(); |
1866 | |
1867 | ASSERT( !FooCount, NULL ); |
1868 | REMARK("sizeof(concurrent_vector<int>) == %d\n" , (int)sizeof(tbb::concurrent_vector<int>)); |
1869 | return Harness::Done; |
1870 | } |
1871 | |
1872 | #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) |
1873 | #pragma warning (pop) |
1874 | #endif // warning 4800 is back |
1875 | |