diff options
Diffstat (limited to 'pstl/test/support/utils.h')
-rw-r--r-- | pstl/test/support/utils.h | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/pstl/test/support/utils.h b/pstl/test/support/utils.h index 541dc8c..090f937 100644 --- a/pstl/test/support/utils.h +++ b/pstl/test/support/utils.h @@ -231,6 +231,82 @@ fill_data(Iterator first, Iterator last, F f) } } +struct MemoryChecker { + // static counters and state tags + static std::atomic<std::int64_t> alive_object_counter; // initialized outside + static constexpr std::int64_t alive_state = 0xAAAAAAAAAAAAAAAA; + static constexpr std::int32_t dead_state = 0; // only used as a set value to cancel alive_state + + std::int32_t _value; // object value used for algorithms + std::int64_t _state; // state tag used for checks + + // ctors, dtors, assign ops + explicit MemoryChecker(std::int32_t value = 0) : _value(value) { + // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since we cannot guarantee that + // raw memory for object being constructed does not have a bit sequence being equal to alive_state + + // set constructed state and increment counter for living object + inc_alive_objects(); + _state = alive_state; + } + MemoryChecker(MemoryChecker&& other) : _value(other.value()) { + // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since + // compiler can optimize out the move ctor call that results in false positive failure + EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker(MemoryChecker&&): attemp to construct an object from non-existing object"); + // set constructed state and increment counter for living object + inc_alive_objects(); + _state = alive_state; + } + MemoryChecker(const MemoryChecker& other) : _value(other.value()) { + // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since + // compiler can optimize out the copy ctor call that results in false positive failure + EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker(const MemoryChecker&): attemp to construct an object from non-existing object"); + // set constructed state and increment counter for living object + inc_alive_objects(); + _state = alive_state; + } + MemoryChecker& operator=(MemoryChecker&& other) { + // check if we do not assign over uninitialized memory + EXPECT_TRUE(state() == alive_state, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign to non-existing object"); + EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign from non-existing object"); + // just assign new value, counter is the same, state is the same + _value = other.value(); + + return *this; + } + MemoryChecker& operator=(const MemoryChecker& other) { + // check if we do not assign over uninitialized memory + EXPECT_TRUE(state() == alive_state, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign to non-existing object"); + EXPECT_TRUE(other.state() == alive_state, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign from non-existing object"); + // just assign new value, counter is the same, state is the same + _value = other.value(); + + return *this; + } + ~MemoryChecker() { + // check if we do not double destruct the object + EXPECT_TRUE(state() == alive_state, "wrong effect from ~MemoryChecker(): attemp to destroy non-existing object"); + // set destructed state and decrement counter for living object + static_cast<volatile std::int64_t&>(_state) = dead_state; + dec_alive_objects(); + } + + // getters + std::int32_t value() const { return _value; } + std::int64_t state() const { return _state; } + static std::int32_t alive_objects() { return alive_object_counter.load(); } +private: + // setters + void inc_alive_objects() { alive_object_counter.fetch_add(1); } + void dec_alive_objects() { alive_object_counter.fetch_sub(1); } +}; + +std::atomic<std::int64_t> MemoryChecker::alive_object_counter{0}; + +std::ostream& operator<<(std::ostream& os, const MemoryChecker& val) { return (os << val.value()); } +bool operator==(const MemoryChecker& v1, const MemoryChecker& v2) { return v1.value() == v2.value(); } +bool operator<(const MemoryChecker& v1, const MemoryChecker& v2) { return v1.value() < v2.value(); } + // Sequence<T> is a container of a sequence of T with lots of kinds of iterators. // Prefixes on begin/end mean: // c = "const" |