1 /*
2     Copyright (c) 2005-2021 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 #ifndef __TBB_test_common_container_move_support_H
18 #define __TBB_test_common_container_move_support_H
19 
20 #include "config.h"
21 
22 #include <vector>
23 #include <memory>
24 #include <type_traits>
25 #include <algorithm>
26 #include "custom_allocators.h"
27 #include "state_trackable.h"
28 
29 namespace move_support_tests {
30 
31 std::atomic<std::size_t> foo_count;
32 std::size_t max_foo_count = 0;
33 static constexpr intptr_t initial_bar = 42;
34 static constexpr std::size_t serial_dead_state = std::size_t(-1);
35 
36 struct limit_foo_count_in_scope {
37     std::size_t previous_state;
38     bool active;
previous_statelimit_foo_count_in_scope39     limit_foo_count_in_scope(std::size_t new_limit, bool an_active = true): previous_state(max_foo_count), active(an_active) {
40         if (active){
41             max_foo_count = new_limit;
42         }
43     }
~limit_foo_count_in_scopelimit_foo_count_in_scope44     ~limit_foo_count_in_scope(){
45         if (active) {
46             max_foo_count = previous_state;
47         }
48     }
49 };
50 
51 template<typename static_counter_allocator_type>
52 struct limit_allocated_items_in_scope {
53     std::size_t previous_state;
54     bool active;
previous_statelimit_allocated_items_in_scope55     limit_allocated_items_in_scope(std::size_t new_limit, bool an_active = true) : previous_state(static_counter_allocator_type::max_items), active(an_active)  {
56         if (active){
57             static_counter_allocator_type::set_limits(new_limit);
58         }
59     }
~limit_allocated_items_in_scopelimit_allocated_items_in_scope60     ~limit_allocated_items_in_scope(){
61         if (active) {
62             static_counter_allocator_type::set_limits(previous_state);
63         }
64     }
65 };
66 
67 template<int line_n>
68 struct track_foo_count {
69     bool active;
70     std::size_t previous_state;
track_foo_counttrack_foo_count71     track_foo_count(): active(true), previous_state(foo_count) { }
~track_foo_counttrack_foo_count72     ~track_foo_count(){
73         if (active){
74             this->verify_no_undestroyed_foo_left_and_dismiss();
75         }
76     }
77 
78     //TODO: ideally in most places this check should be replaced with "no foo created or destroyed"
79     //TODO: deactivation of the check seems like a hack
verify_no_undestroyed_foo_left_and_dismisstrack_foo_count80     void verify_no_undestroyed_foo_left_and_dismiss() {
81         REQUIRE_MESSAGE( foo_count == previous_state, "Some instances of Foo were not destroyed ?" );
82         active = false;
83     }
84 };
85 
86 template<typename static_counter_allocator_type>
87 struct track_allocator_memory {
88     using counters_type = typename static_counter_allocator_type::counters_type;
89 
90     counters_type previous_state;
track_allocator_memorytrack_allocator_memory91     track_allocator_memory() { static_counter_allocator_type::init_counters(); }
~track_allocator_memorytrack_allocator_memory92     ~track_allocator_memory(){verify_no_allocator_memory_leaks();}
93 
verify_no_allocator_memory_leakstrack_allocator_memory94     void verify_no_allocator_memory_leaks() const{
95         REQUIRE_MESSAGE( static_counter_allocator_type::items_allocated == static_counter_allocator_type::items_freed, "memory leak?" );
96         REQUIRE_MESSAGE( static_counter_allocator_type::allocations == static_counter_allocator_type::frees, "memory leak?" );
97     }
save_allocator_counterstrack_allocator_memory98     void save_allocator_counters(){ previous_state = static_counter_allocator_type::counters(); }
verify_no_more_than_x_memory_items_allocatedtrack_allocator_memory99     void verify_no_more_than_x_memory_items_allocated(std::size_t  expected_number_of_items_to_allocate){
100         counters_type now = static_counter_allocator_type::counters();
101         REQUIRE_MESSAGE( (now.items_allocated - previous_state.items_allocated) <= expected_number_of_items_to_allocate, "More then excepted memory allocated ?" );
102     }
103 };
104 #if TBB_USE_EXCEPTIONS
105 struct FooException : std::bad_alloc {
whatFooException106     virtual const char* what() const noexcept override { return "out of Foo limit"; }
~FooExceptionFooException107     virtual ~FooException() {}
108 };
109 #endif
110 
111 struct FooLimit {
FooLimitFooLimit112     FooLimit() {
113         if (max_foo_count && foo_count >= max_foo_count) {
114             TBB_TEST_THROW(FooException{});
115         }
116     }
117 };
118 
119 // TODO: consider better naming
120 class Foo : FooLimit, public StateTrackable</*allow_zero_initialized = */true> {
121 protected:
122     using state_trackable_type = StateTrackable<true>;
123     intptr_t my_bar;
124     std::size_t my_serial{};
125     std::size_t my_thread_id{};
126 public:
127 
is_valid_or_zero()128     bool is_valid_or_zero() const {
129         return is_valid() || (state == ZeroInitialized && !my_bar);
130     }
131 
zero_bar()132     intptr_t& zero_bar() {
133         CHECK_FAST(is_valid_or_zero());
134         return my_bar;
135     }
136 
zero_bar()137     intptr_t zero_bar() const {
138         CHECK_FAST(is_valid_or_zero());
139         return my_bar;
140     }
141 
bar()142     intptr_t& bar() {
143         CHECK_FAST(is_valid());
144         return my_bar;
145     }
146 
bar()147     intptr_t bar() const {
148         CHECK_FAST(is_valid());
149         return my_bar;
150     }
151 
set_serial(std::size_t s)152     void set_serial( std::size_t s ) {
153         my_serial = s;
154     }
155 
get_serial()156     std::size_t get_serial() const {
157         return my_serial;
158     }
159 
set_thread_id(std::size_t t)160     void set_thread_id( std::size_t t ) {
161         my_thread_id = t;
162     }
163 
get_thread_id()164     std::size_t get_thread_id() const {
165         return my_thread_id;
166     }
167 
intptr_t()168     operator intptr_t() const { return bar(); }
169 
Foo(intptr_t br)170     Foo( intptr_t br ) : state_trackable_type(0) {
171         my_bar = br;
172         ++foo_count;
173     }
174 
Foo()175     Foo() {
176         my_bar = initial_bar;
177         ++foo_count;
178     }
179 
Foo(const Foo & foo)180     Foo( const Foo& foo ) : state_trackable_type(foo) {
181         my_bar = foo.my_bar;
182         ++foo_count;
183         my_serial = foo.my_serial;
184         my_thread_id = foo.my_thread_id;
185     }
186 
Foo(Foo && foo)187     Foo( Foo&& foo ) : state_trackable_type(std::move(foo)) {
188         my_bar = foo.my_bar;
189         my_serial = foo.my_serial;
190         my_thread_id = foo.my_thread_id;
191         ++foo_count;
192     }
193 
~Foo()194     ~Foo() {
195         my_bar = ~initial_bar;
196         my_serial = serial_dead_state;
197         my_thread_id = serial_dead_state;
198         if (state != ZeroInitialized) {
199             --foo_count;
200         }
201     }
202 
203     Foo& operator=( const Foo& x ) {
204         state_trackable_type::operator=(x);
205         my_bar = x.my_bar;
206         my_serial = x.my_serial;
207         my_thread_id = x.my_thread_id;
208         return *this;
209     }
210 
211     Foo& operator=( Foo&& x ) {
212         state_trackable_type::operator=(std::move(x));
213         my_bar = x.my_bar;
214         my_serial = x.my_serial;
215         my_thread_id = x.my_thread_id;
216         x.my_serial = serial_dead_state;
217         x.my_thread_id = serial_dead_state;
218         x.my_bar = -1;
219         return *this;
220     }
221 
222     friend bool operator==( const int& lhs, const Foo& rhs ) {
223         CHECK_FAST_MESSAGE(rhs.is_valid_or_zero(), "Comparing invalid objects");
224         return lhs == rhs.my_bar;
225     }
226 
227     friend bool operator==( const Foo& lhs, const int& rhs ) {
228         CHECK_FAST_MESSAGE(lhs.is_valid_or_zero(), "Comparing invalid objects");
229         return lhs.my_bar == rhs;
230     }
231 
232     friend bool operator==( const Foo& lhs, const Foo& rhs ) {
233         CHECK_FAST_MESSAGE(lhs.is_valid_or_zero(), "Comparing invalid objects");
234         CHECK_FAST_MESSAGE(rhs.is_valid_or_zero(), "Comparing invalid objects");
235         return lhs.my_bar == rhs.my_bar;
236     }
237 
238     friend bool operator<( const Foo& lhs, const Foo& rhs ) {
239         CHECK_FAST_MESSAGE(lhs.is_valid_or_zero(), "Comparing invalid objects");
240         CHECK_FAST_MESSAGE(rhs.is_valid_or_zero(), "Comparing invalid objects");
241         return lhs.my_bar < rhs.my_bar;
242     }
243 
is_const()244     bool is_const() const { return true; }
is_const()245     bool is_const() { return false; }
246 
247 protected:
248     char reserve[1];
249 }; // struct Foo
250 
251 struct FooWithAssign : Foo {
252     FooWithAssign() = default;
FooWithAssignFooWithAssign253     FooWithAssign( intptr_t b ) : Foo(b) {}
254     FooWithAssign( const FooWithAssign& ) = default;
255     FooWithAssign( FooWithAssign&& ) = default;
256 
257     FooWithAssign& operator=( const FooWithAssign& f ) {
258         return static_cast<FooWithAssign&>(Foo::operator=(f));
259     }
260 
261     FooWithAssign& operator=( FooWithAssign&& f ) {
262         return static_cast<FooWithAssign&>(Foo::operator=(std::move(f)));
263     }
264 }; // struct FooWithAssign
265 
266 template <typename FooIteratorType>
267 class FooIteratorBase {
268 protected:
269     intptr_t x_bar;
270 private:
as_derived()271     FooIteratorType& as_derived() { return *static_cast<FooIteratorType*>(this); }
272 public:
FooIteratorBase(intptr_t x)273     FooIteratorBase( intptr_t x ) : x_bar(x) {}
274 
275     FooIteratorType& operator++() {
276         ++x_bar;
277         return as_derived();
278     }
279 
280     FooIteratorType operator++(int) {
281         FooIteratorType tmp(as_derived());
282         ++x_bar;
283         return tmp;
284     }
285 
286     friend bool operator==( const FooIteratorType& lhs, const FooIteratorType& rhs ) {
287         return lhs.x_bar == rhs.x_bar;
288     }
289 
290     friend bool operator!=( const FooIteratorType& lhs, const FooIteratorType& rhs ) {
291         return !(lhs == rhs);
292     }
293 }; // class FooIteratorBase
294 
295 class FooIterator : public FooIteratorBase<FooIterator> {
296     using base_type = FooIteratorBase<FooIterator>;
297 public:
298     using iterator_category = std::input_iterator_tag;
299     using value_type = FooWithAssign;
300     using difference_type = std::ptrdiff_t;
301     using pointer = value_type*;
302     using reference = value_type&;
303 
304     using base_type::base_type;
305 
306     value_type operator*() {
307         return value_type(x_bar);
308     }
309 }; // class FooIterator
310 
311 class FooPairIterator : public FooIteratorBase<FooPairIterator> {
312     using base_type = FooIteratorBase<FooPairIterator>;
313 public:
314     using iterator_category = std::input_iterator_tag;
315     using value_type = std::pair<FooWithAssign, FooWithAssign>;
316     using difference_type = std::ptrdiff_t;
317     using pointer = value_type*;
318     using reference = value_type&;
319 
320     using base_type::base_type;
321 
322     value_type operator*() {
323         FooWithAssign foo;
324         foo.bar() = x_bar;
325         return std::make_pair(foo, foo);
326     }
327 }; // class FooPairIterator
328 
329 struct MemoryLocations {
330     std::vector<const void*> locations;
331 
332     template <typename ContainerType>
MemoryLocationsMemoryLocations333     MemoryLocations( const ContainerType& source ) : locations(source.size()) {
334         for (auto it = source.begin(); it != source.end(); ++it) {
335             locations[std::distance(source.begin(), it)] = &*it;
336         }
337     }
338 
339     template <typename ContainerType>
content_location_unchangedMemoryLocations340     bool content_location_unchanged( const ContainerType& dst ) {
341         auto is_same_location = []( const typename ContainerType::value_type& v, const void* location ) {
342             return &v == location;
343         };
344 
345         return std::equal(dst.begin(), dst.end(), locations.begin(), is_same_location);
346     }
347 
348     template <typename ContainerType>
content_location_changedMemoryLocations349     bool content_location_changed( const ContainerType& dst ) {
350         auto is_not_same_location = []( const typename ContainerType::value_type& v, const void* location ) {
351             return &v != location;
352         };
353         return std::equal(dst.begin(), dst.end(), locations.begin(), is_not_same_location);
354     }
355 }; // struct MemoryLocations
356 
357 template <typename T, typename POCMA = std::false_type>
358 struct ArenaAllocatorFixture {
359     using allocator_type = ArenaAllocator<T, POCMA>;
360     using arena_data_type = typename allocator_type::arena_data_type;
361 
362     std::vector<typename std::aligned_storage<sizeof(T)>::type> storage;
363     arena_data_type arena_data;
364     allocator_type allocator;
365 
ArenaAllocatorFixtureArenaAllocatorFixture366     ArenaAllocatorFixture( std::size_t size_to_allocate )
367         : storage(size_to_allocate),
368           arena_data(reinterpret_cast<T*>(&storage.front()), storage.size()),
369           allocator(arena_data) {}
370 
371     ArenaAllocatorFixture( const ArenaAllocatorFixture& ) = delete;
372 }; // struct ArenaAllocatorFixture
373 
374 template <typename T, typename POCMA = std::false_type>
375 struct TwoMemoryArenasFixture {
376     using arena_fixture_type = ArenaAllocatorFixture<T, POCMA>;
377     using allocator_type = typename arena_fixture_type::allocator_type;
378 
379     arena_fixture_type source_arena_fixture;
380     arena_fixture_type dst_arena_fixture;
381 
382     allocator_type& source_allocator;
383     allocator_type& dst_allocator;
384 
TwoMemoryArenasFixtureTwoMemoryArenasFixture385     TwoMemoryArenasFixture( std::size_t size_to_allocate )
386         : source_arena_fixture(size_to_allocate),
387           dst_arena_fixture(size_to_allocate),
388           source_allocator(source_arena_fixture.allocator),
389           dst_allocator(dst_arena_fixture.allocator)
390     {
391         REQUIRE_MESSAGE(&(*source_arena_fixture.storage.begin()) != &(*dst_arena_fixture.storage.begin()),
392                         "source and destination arena instances should use difference memory regions");
393         REQUIRE_MESSAGE(source_allocator != dst_allocator, "arenas using difference memory regions should not compare equal");
394         using Traits_POCMA = typename tbb::detail::allocator_traits<allocator_type>::propagate_on_container_move_assignment;
395         REQUIRE_MESSAGE(POCMA::value == Traits_POCMA::value,
396                         "POCMA::value should be the same as in allocator_traits");
397 
398         allocator_type source_allocator_copy(source_allocator);
399         allocator_type dst_allocator_copy(dst_allocator);
400         allocator_type source_previous_state(source_allocator);
401 
402         REQUIRE_MESSAGE(source_previous_state == source_allocator,
403                         "Copy of the allocator should compare equal with it's source");
404         dst_allocator_copy = std::move(source_allocator_copy);
405         REQUIRE_MESSAGE(dst_allocator_copy == source_previous_state,
406                         "Move initialized allocator should compare equal with it's source before movement");
407     }
408 
409     TwoMemoryArenasFixture( const TwoMemoryArenasFixture& ) = delete;
410 
verify_allocator_was_movedTwoMemoryArenasFixture411     void verify_allocator_was_moved( const allocator_type& result_allocator ) {
412         // TODO: add assert that move ctor/assignment was called
413         REQUIRE_MESSAGE(result_allocator == source_allocator, "allocator was not moved");
414         REQUIRE_MESSAGE(result_allocator != dst_allocator, "allocator_was_not_moved");
415     }
416 
417 }; // struct TwoMemoryArenasFixture
418 
419 template <typename ContainerTraits, typename Allocator>
420 struct MoveFixture {
421     using element_type = typename Allocator::value_type;
422     using container_value_type = typename ContainerTraits::template container_value_type<element_type>;
423     using allocator_type = typename tbb::detail::allocator_traits<Allocator>::template rebind_alloc<container_value_type>;
424     using container_type = typename ContainerTraits::template container_type<element_type, allocator_type>;
425     using init_iterator_type = typename ContainerTraits::init_iterator_type;
426 
427     static constexpr std::size_t default_container_size = 100;
428     const std::size_t container_size;
429 
430     typename std::aligned_storage<sizeof(container_type)>::type source_storage;
431     container_type& source;
432 
433     MemoryLocations locations;
434 
435     MoveFixture( std::size_t cont_size = default_container_size )
container_sizeMoveFixture436         : container_size(cont_size),
437           source(ContainerTraits::template construct_container<container_type>(source_storage,
438                                                                                init_iterator_type(0), init_iterator_type(cont_size))),
439           locations(source)
440     {
441         init();
442     }
443 
444     MoveFixture( const Allocator& a, std::size_t cont_size = default_container_size )
container_sizeMoveFixture445         : container_size(cont_size),
446           source(ContainerTraits::template construct_container<container_type>(source_storage, init_iterator_type(0),
447                                                                                init_iterator_type(cont_size), a)),
448           locations(source)
449     {
450         init();
451     }
452 
453     MoveFixture( const MoveFixture& ) = delete;
454 
~MoveFixtureMoveFixture455     ~MoveFixture() {
456         reinterpret_cast<container_type*>(&source)->~container_type();
457     }
458 
initMoveFixture459     void init() {
460         verify_size(source);
461         verify_content_equal_to_source(source);
462         verify_size(locations.locations);
463     }
464 
content_location_unchangedMoveFixture465     bool content_location_unchanged( const container_type& dst ) {
466         return locations.content_location_unchanged(dst);
467     }
468 
content_location_changedMoveFixture469     bool content_location_changed( const container_type& dst ) {
470         return locations.content_location_changed(dst);
471     }
472 
473     template <typename ContainerType>
verify_sizeMoveFixture474     void verify_size( const ContainerType& dst ) {
475         REQUIRE(container_size == dst.size());
476     }
477 
verify_content_equal_to_sourceMoveFixture478     void verify_content_equal_to_source( const container_type& dst ) {
479         REQUIRE(ContainerTraits::equal(dst, init_iterator_type(0), init_iterator_type(container_size)));
480     }
481 
verify_content_equal_to_sourceMoveFixture482     void verify_content_equal_to_source( const container_type& dst, std::size_t number_of_constructed_items ) {
483         REQUIRE(number_of_constructed_items <= dst.size());
484         REQUIRE(std::equal(dst.begin(), dst.begin() + number_of_constructed_items, init_iterator_type(0)));
485     }
486 
verify_content_shallow_movedMoveFixture487     void verify_content_shallow_moved( const container_type& dst ) {
488         verify_size(dst);
489         REQUIRE_MESSAGE(content_location_unchanged(dst), "Container move ctor actually changed element locations, while should not");
490         REQUIRE_MESSAGE(source.empty(), "Moved from container should not contain any elements");
491         verify_content_equal_to_source(dst);
492     }
493 
verify_content_deep_movedMoveFixture494     void verify_content_deep_moved( const container_type& dst ) {
495         verify_size(dst);
496         REQUIRE_MESSAGE(content_location_changed(dst), "Container did not changed element locations for unequal allocators");
497         REQUIRE_MESSAGE(std::all_of(dst.begin(), dst.end(), is_state_predicate<Foo::MoveInitialized>()),
498                         "Container did not move construct some elements");
499         REQUIRE_MESSAGE(std::all_of(source.begin(), source.end(), is_state_predicate<Foo::MovedFrom>()),
500                         "Container did not move all the elements");
501         verify_content_equal_to_source(dst);
502     }
503 
verify_part_of_content_deep_movedMoveFixture504     void verify_part_of_content_deep_moved(container_type const& dst, std::size_t number_of_constructed_items){
505         REQUIRE_MESSAGE(content_location_changed(dst), "Vector actually did not changed element locations for unequal allocators, while should");
506         REQUIRE_MESSAGE(std::all_of(dst.begin(), dst.begin() + number_of_constructed_items, is_state_predicate<Foo::MoveInitialized>{}), "Vector did not move construct some elements?");
507         if (dst.size() != number_of_constructed_items) {
508             REQUIRE_MESSAGE(std::all_of(dst.begin() + number_of_constructed_items, dst.end(), is_state_predicate<Foo::ZeroInitialized>{}), "Failed to zero-initialize items left not constructed after the exception?" );
509         }
510         verify_content_equal_to_source(dst, number_of_constructed_items);
511 
512         REQUIRE_MESSAGE(std::all_of(source.begin(), source.begin() + number_of_constructed_items, is_state_predicate<Foo::MovedFrom>{}),  "Vector did not move all the elements?");
513         REQUIRE_MESSAGE(std::all_of(source.begin() + number_of_constructed_items, source.end(), is_not_state_predicate<Foo::MovedFrom>{}),  "Vector changed elements in source after exception point?");
514     }
515 }; // struct MoveFixture
516 
517 template <typename StaticCountingAllocatorType>
518 struct TrackAllocatorMemory {
519     using counters_type = typename StaticCountingAllocatorType::counters_type;
520 
521     counters_type previous_state;
522 
TrackAllocatorMemoryTrackAllocatorMemory523     TrackAllocatorMemory() {
524         StaticCountingAllocatorType::init_counters();
525     }
526 
527     TrackAllocatorMemory( const TrackAllocatorMemory& ) = delete;
528 
~TrackAllocatorMemoryTrackAllocatorMemory529     ~TrackAllocatorMemory() {
530         verify_no_allocator_memory_leaks();
531     }
532 
verify_no_allocator_memory_leaksTrackAllocatorMemory533     void verify_no_allocator_memory_leaks() const {
534         REQUIRE_MESSAGE(StaticCountingAllocatorType::items_allocated == StaticCountingAllocatorType::items_freed, "Memory leak");
535         REQUIRE_MESSAGE(StaticCountingAllocatorType::allocations == StaticCountingAllocatorType::frees, "Memory leak");
536         REQUIRE_MESSAGE(StaticCountingAllocatorType::items_constructed == StaticCountingAllocatorType::items_destroyed,
537                         "The number of constructed items is not equal to the number of destroyed items");
538     }
539 
save_allocator_countersTrackAllocatorMemory540     void save_allocator_counters() { previous_state = StaticCountingAllocatorType::counters(); }
541 
verify_no_more_than_x_memory_items_allocatedTrackAllocatorMemory542     void verify_no_more_than_x_memory_items_allocated( std::size_t expected ) {
543         counters_type now = StaticCountingAllocatorType::counters();
544         REQUIRE_MESSAGE((now.items_allocated - previous_state.items_allocated) <= expected,
545                         "More then expected memory allocated");
546     }
547 }; // struct TrackAllocatorMemory
548 
549 struct TrackFooCount {
TrackFooCountTrackFooCount550     TrackFooCount() : active(true), previous_state(foo_count) {}
551 
552     TrackFooCount( const TrackFooCount& ) = delete;
553 
~TrackFooCountTrackFooCount554     ~TrackFooCount() {
555         if (active) {
556             verify_no_undestroyed_foo_left_and_dismiss();
557         }
558     }
559 
verify_no_undestroyed_foo_left_and_dismissTrackFooCount560     void verify_no_undestroyed_foo_left_and_dismiss() {
561         REQUIRE_MESSAGE(foo_count == previous_state, "Some instances of Foo were not destroyed");
562         active = false;
563     }
564 
565     bool active;
566     std::size_t previous_state;
567 }; // struct TrackFooCount
568 
569 template <typename ContainerTraits, typename POCMA = std::false_type, typename T = FooWithAssign>
570 struct DefaultStatefulFixtureHelper {
571     using allocator_fixture_type = TwoMemoryArenasFixture<T, POCMA>;
572     using allocator_type = StaticSharedCountingAllocator<typename allocator_fixture_type::allocator_type>;
573 
574     using move_fixture_type = MoveFixture<ContainerTraits, allocator_type>;
575 
576     using leaks_tracker_type = TrackAllocatorMemory<allocator_type>;
577     using foo_leaks_in_test_tracker_type = TrackFooCount;
578 
579     struct DefaultStatefulFixture
580         : leaks_tracker_type,
581           allocator_fixture_type,
582           move_fixture_type,
583           foo_leaks_in_test_tracker_type
584     {
585         //TODO: calculate needed size for allocator_fixture_type more accurately
586         //allocate twice more storage to handle case when copy constructor called instead of move one
DefaultStatefulFixtureDefaultStatefulFixtureHelper::DefaultStatefulFixture587         DefaultStatefulFixture()
588             : leaks_tracker_type(),
589               allocator_fixture_type(2 * 4 * move_fixture_type::default_container_size),
590               move_fixture_type(allocator_fixture_type::source_allocator),
591               foo_leaks_in_test_tracker_type()
592         {
593             leaks_tracker_type::save_allocator_counters();
594         }
595 
verify_no_more_than_x_memory_items_allocatedDefaultStatefulFixtureHelper::DefaultStatefulFixture596         void verify_no_more_than_x_memory_items_allocated() {
597             auto n = ContainerTraits::expected_number_of_items_to_allocate_for_steal_move;
598             leaks_tracker_type::verify_no_more_than_x_memory_items_allocated(n);
599         }
600 
601         using allocator_type = typename move_fixture_type::container_type::allocator_type;
602     }; // struct DefaultStatefulFixture
603 
604     using type = DefaultStatefulFixture;
605 }; // struct DefaultStatefulFixtureHelper
606 
607 template <typename StaticCountingAllocatorType>
608 struct LimitAllocatedItemsInScope {
609     LimitAllocatedItemsInScope( std::size_t limit, bool act = true )
previous_stateLimitAllocatedItemsInScope610         : previous_state(StaticCountingAllocatorType::max_items), active(act)
611     {
612         if (active) {
613             StaticCountingAllocatorType::set_limits(limit);
614         }
615     }
616 
617     LimitAllocatedItemsInScope( const LimitAllocatedItemsInScope& ) = delete;
618 
~LimitAllocatedItemsInScopeLimitAllocatedItemsInScope619     ~LimitAllocatedItemsInScope() {
620         if (active) {
621             StaticCountingAllocatorType::set_limits(previous_state);
622         }
623     }
624 
625     std::size_t previous_state;
626     bool active;
627 }; // struct LimitAllocatedItemsInScope
628 
629 struct LimitFooCountInScope {
630     LimitFooCountInScope( std::size_t limit, bool act = true )
previous_stateLimitFooCountInScope631         : previous_state(max_foo_count), active(act)
632     {
633         if (active) {
634             max_foo_count = limit;
635         }
636     }
637 
638     LimitFooCountInScope( const LimitFooCountInScope& ) = delete;
639 
~LimitFooCountInScopeLimitFooCountInScope640     ~LimitFooCountInScope() {
641         if (active) {
642             max_foo_count = previous_state;
643         }
644     }
645 
646     std::size_t previous_state;
647     bool active;
648 }; // struct LimitFooCountInScope
649 
650 template <typename ContainerTraits>
test_move_ctor_single_argument()651 void test_move_ctor_single_argument() {
652     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits>::type;
653     using container_type = typename fixture_type::container_type;
654 
655     fixture_type fixture;
656 
657     container_type dst(std::move(fixture.source));
658 
659     fixture.verify_content_shallow_moved(dst);
660     fixture.verify_allocator_was_moved(dst.get_allocator());
661     fixture.verify_no_more_than_x_memory_items_allocated();
662     fixture.verify_no_undestroyed_foo_left_and_dismiss();
663 }
664 
665 template <typename ContainerTraits>
test_move_ctor_with_equal_allocator()666 void test_move_ctor_with_equal_allocator() {
667     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits>::type;
668     using container_type = typename fixture_type::container_type;
669 
670     fixture_type fixture;
671 
672     container_type dst(std::move(fixture.source), fixture.source.get_allocator());
673 
674     fixture.verify_content_shallow_moved(dst);
675     fixture.verify_no_more_than_x_memory_items_allocated();
676     fixture.verify_no_undestroyed_foo_left_and_dismiss();
677 }
678 
679 template <typename ContainerTraits>
test_move_ctor_with_unequal_allocator()680 void test_move_ctor_with_unequal_allocator() {
681     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits>::type;
682     using container_type = typename fixture_type::container_type;
683 
684     fixture_type fixture;
685 
686     typename container_type::allocator_type alloc(fixture.dst_allocator);
687     container_type dst(std::move(fixture.source), alloc);
688 
689     fixture.verify_content_deep_moved(dst);
690 }
691 
692 template <typename ContainerTraits>
test_move_assignment_POCMA_true_stateful_allocator()693 void test_move_assignment_POCMA_true_stateful_allocator() {
694     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits, /*POCMA = */std::true_type>::type;
695     using container_type = typename fixture_type::container_type;
696 
697     fixture_type fixture;
698 
699     container_type dst(fixture.dst_allocator);
700     dst = std::move(fixture.source);
701 
702     fixture.verify_content_shallow_moved(dst);
703     fixture.verify_allocator_was_moved(dst.get_allocator());
704     fixture.verify_no_more_than_x_memory_items_allocated();
705     fixture.verify_no_undestroyed_foo_left_and_dismiss();
706 }
707 
708 template <typename ContainerTraits>
test_move_assignment_POCMA_true_stateless_allocator()709 void test_move_assignment_POCMA_true_stateless_allocator() {
710     // POCMA is true for std::allocator since C++14, is_always_equal is true since C++17
711     // Behavior can be unexpected, TODO: consider another allocator type
712     using allocator_type = std::allocator<FooWithAssign>;
713     using fixture_type = MoveFixture<ContainerTraits, allocator_type>;
714     using container_type = typename fixture_type::container_type;
715 
716     fixture_type fixture;
717 
718     REQUIRE_MESSAGE(fixture.source.get_allocator() == allocator_type(), "Incorrect test setup: allocator is stateful");
719 
720     container_type dst;
721     dst = std::move(fixture.source);
722 
723     fixture.verify_content_shallow_moved(dst);
724 }
725 
726 template <typename ContainerTraits>
test_move_assignment_POCMA_false_equal_allocator()727 void test_move_assignment_POCMA_false_equal_allocator() {
728     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits, /*POCMA = */std::false_type>::type;
729     using container_type = typename fixture_type::container_type;
730 
731     fixture_type fixture;
732     container_type dst(fixture.source_allocator);
733     REQUIRE_MESSAGE(fixture.source.get_allocator() == dst.get_allocator(), "Incorrect test setup: allocators should be equal");
734 
735     fixture.save_allocator_counters();
736 
737     dst = std::move(fixture.source);
738 
739     fixture.verify_content_shallow_moved(dst);
740     fixture.verify_no_more_than_x_memory_items_allocated();
741     fixture.verify_no_undestroyed_foo_left_and_dismiss();
742 }
743 
744 template <typename ContainerTraits>
test_move_assignment_POCMA_false_unequal_allocator()745 void test_move_assignment_POCMA_false_unequal_allocator() {
746     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits, /*POCMA = */std::false_type>::type;
747     using container_type = typename fixture_type::container_type;
748 
749     fixture_type fixture;
750 
751     container_type dst(fixture.dst_allocator);
752     dst = std::move(fixture.source);
753 
754     fixture.verify_content_deep_moved(dst);
755 }
756 
757 #define REQUIRE_THROW_EXCEPTION(expr, exception_type)            \
758         try {                                                    \
759             expr;                                                \
760             REQUIRE_MESSAGE(false, "Exception should be thrown");\
761         } catch (exception_type&) {                              \
762         } catch (...) {                                          \
763             REQUIRE_MESSAGE(false, "Unexpected exception");      \
764         }                                                        \
765 
766 #if TBB_USE_EXCEPTIONS
767 template <typename ContainerTraits>
test_ex_move_ctor_unequal_allocator_memory_failure()768 void test_ex_move_ctor_unequal_allocator_memory_failure() {
769     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits>::type;
770     using container_type = typename fixture_type::container_type;
771     using allocator_type = typename container_type::allocator_type;
772 
773     fixture_type fixture;
774 
775     std::size_t limit = allocator_type::items_allocated + fixture.container_size / 4;
776     LimitAllocatedItemsInScope<allocator_type> alloc_limit(limit);
777 
778     REQUIRE_THROW_EXCEPTION(container_type dst(std::move(fixture.source), fixture.dst_allocator), std::bad_alloc);
779 }
780 
781 template <typename ContainerTraits>
test_ex_move_ctor_unequal_allocator_element_ctor_failure()782 void test_ex_move_ctor_unequal_allocator_element_ctor_failure() {
783     using fixture_type = typename DefaultStatefulFixtureHelper<ContainerTraits>::type;
784     using container_type = typename fixture_type::container_type;
785 
786     fixture_type fixture;
787 
788     std::size_t limit = foo_count + fixture.container_size / 4;
789     LimitFooCountInScope foo_limit(limit);
790     REQUIRE_THROW_EXCEPTION(container_type dst(std::move(fixture.source), fixture.dst_allocator), FooException);
791 }
792 
793 template <typename ContainerTraits>
test_ex_move_constructor()794 void test_ex_move_constructor() {
795     test_ex_move_ctor_unequal_allocator_memory_failure<ContainerTraits>();
796     test_ex_move_ctor_unequal_allocator_element_ctor_failure<ContainerTraits>();
797     // TODO: add test for move assignment exceptions
798 }
799 #endif
800 
801 template <typename ContainerTraits>
test_move_constructor()802 void test_move_constructor() {
803     test_move_ctor_single_argument<ContainerTraits>();
804     test_move_ctor_with_equal_allocator<ContainerTraits>();
805     test_move_ctor_with_unequal_allocator<ContainerTraits>();
806 }
807 
808 template <typename ContainerTraits>
test_move_assignment()809 void test_move_assignment() {
810     test_move_assignment_POCMA_true_stateful_allocator<ContainerTraits>();
811     test_move_assignment_POCMA_true_stateless_allocator<ContainerTraits>();
812     test_move_assignment_POCMA_false_equal_allocator<ContainerTraits>();
813     test_move_assignment_POCMA_false_unequal_allocator<ContainerTraits>();
814 }
815 
816 template<typename container_traits>
test_constructor_with_move_iterators()817 void test_constructor_with_move_iterators(){
818     using fixture_type = typename move_support_tests::DefaultStatefulFixtureHelper<container_traits>::type;
819     using container_type = typename fixture_type::container_type;
820 
821     fixture_type fixture;
822 
823     container_type dst(std::make_move_iterator(fixture.source.begin()), std::make_move_iterator(fixture.source.end()), fixture.dst_allocator);
824 
825     fixture.verify_content_deep_moved(dst);
826 }
827 
828 template<typename container_traits>
test_assign_with_move_iterators()829 void test_assign_with_move_iterators(){
830     using fixture_type = typename move_support_tests::DefaultStatefulFixtureHelper<container_traits>::type;
831     using container_type = typename fixture_type::container_type;
832 
833     fixture_type fixture;
834 
835     container_type dst(fixture.dst_allocator);
836     dst.assign(std::make_move_iterator(fixture.source.begin()), std::make_move_iterator(fixture.source.end()));
837 
838     fixture.verify_content_deep_moved(dst);
839 }
840 
841 } // namespace move_support_tests
842 
843 namespace std {
844 template <>
845 struct hash<move_support_tests::Foo> {
846     std::size_t operator()( const move_support_tests::Foo& f ) const {
847         return std::size_t(f.bar());
848     }
849 };
850 
851 template <>
852 struct hash<move_support_tests::FooWithAssign> {
853     std::size_t operator()( const move_support_tests::FooWithAssign& f ) const {
854         return std::hash<move_support_tests::Foo>{}(f);
855     }
856 };
857 }
858 
859 #endif // __TBB_test_common_container_move_support_H
860