151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
1751c0b2f7Stbbdev //! \file test_intrusive_list.cpp
1851c0b2f7Stbbdev //! \brief Test for [internal] functionality
1951c0b2f7Stbbdev
2051c0b2f7Stbbdev #include "common/test.h"
2151c0b2f7Stbbdev #include "common/utils.h"
2251c0b2f7Stbbdev #include "../../src/tbb/intrusive_list.h"
2351c0b2f7Stbbdev
2451c0b2f7Stbbdev using tbb::detail::r1::intrusive_list_node;
2551c0b2f7Stbbdev
2651c0b2f7Stbbdev // Machine word filled with repeated pattern of FC bits
2751c0b2f7Stbbdev const uintptr_t NoliMeTangere = ~uintptr_t(0)/0xFF*0xFC;
2851c0b2f7Stbbdev
2951c0b2f7Stbbdev struct VerificationBase : utils::NoAfterlife {
3051c0b2f7Stbbdev uintptr_t m_Canary;
VerificationBaseVerificationBase3151c0b2f7Stbbdev VerificationBase() : m_Canary(NoliMeTangere) {}
3251c0b2f7Stbbdev }; // struct VerificationBase
3351c0b2f7Stbbdev
3451c0b2f7Stbbdev struct DataItemWithInheritedNodeBase : intrusive_list_node {
3551c0b2f7Stbbdev int m_Data;
3651c0b2f7Stbbdev
DataItemWithInheritedNodeBaseDataItemWithInheritedNodeBase3751c0b2f7Stbbdev DataItemWithInheritedNodeBase( int value ) : m_Data(value) {}
3851c0b2f7Stbbdev
DataDataItemWithInheritedNodeBase3951c0b2f7Stbbdev int Data() const { return m_Data; }
4051c0b2f7Stbbdev }; // struct DataItemWithInheritedNodeBase
4151c0b2f7Stbbdev
4251c0b2f7Stbbdev struct DataItemWithInheritedNode : VerificationBase, DataItemWithInheritedNodeBase {
4351c0b2f7Stbbdev friend class tbb::detail::r1::intrusive_list<DataItemWithInheritedNode>;
4451c0b2f7Stbbdev
DataItemWithInheritedNodeDataItemWithInheritedNode4551c0b2f7Stbbdev DataItemWithInheritedNode( int value ) : DataItemWithInheritedNodeBase(value) {}
4651c0b2f7Stbbdev }; // struct DataItemWithInheritedNode
4751c0b2f7Stbbdev
4851c0b2f7Stbbdev struct DataItemWithMemberNodeBase {
4951c0b2f7Stbbdev int m_Data;
5051c0b2f7Stbbdev
5151c0b2f7Stbbdev // Cannot be used be member_intrusive_list to form lists of objects derived from DataItemBase
5251c0b2f7Stbbdev intrusive_list_node m_BaseNode;
5351c0b2f7Stbbdev
DataItemWithMemberNodeBaseDataItemWithMemberNodeBase5451c0b2f7Stbbdev DataItemWithMemberNodeBase( int value ) : m_Data(value) {}
5551c0b2f7Stbbdev
DataDataItemWithMemberNodeBase5651c0b2f7Stbbdev int Data() const { return m_Data; }
5751c0b2f7Stbbdev }; // struct DataItemWithMemberNodeBase
5851c0b2f7Stbbdev
5951c0b2f7Stbbdev struct DataItemWithMemberNodes : VerificationBase, DataItemWithMemberNodeBase {
6051c0b2f7Stbbdev intrusive_list_node m_Node;
6151c0b2f7Stbbdev
DataItemWithMemberNodesDataItemWithMemberNodes6251c0b2f7Stbbdev DataItemWithMemberNodes( int value ) : DataItemWithMemberNodeBase(value) {}
6351c0b2f7Stbbdev }; // struct DataItemWithMemberNodes
6451c0b2f7Stbbdev
6551c0b2f7Stbbdev using intrusive_list1 = tbb::detail::r1::intrusive_list<DataItemWithInheritedNode>;
6651c0b2f7Stbbdev using intrusive_list2 = tbb::detail::r1::memptr_intrusive_list<DataItemWithMemberNodes,
6751c0b2f7Stbbdev DataItemWithMemberNodeBase,
6851c0b2f7Stbbdev &DataItemWithMemberNodeBase::m_BaseNode>;
6951c0b2f7Stbbdev
7051c0b2f7Stbbdev using intrusive_list3 = tbb::detail::r1::memptr_intrusive_list<DataItemWithMemberNodes,
7151c0b2f7Stbbdev DataItemWithMemberNodes,
7251c0b2f7Stbbdev &DataItemWithMemberNodes::m_Node>;
7351c0b2f7Stbbdev
7451c0b2f7Stbbdev const int NumElements = 256 * 1024;
7551c0b2f7Stbbdev
7651c0b2f7Stbbdev // Iterates through the list forward and backward checking the validity of values stored by the list nodes
7751c0b2f7Stbbdev template <typename List, typename Iterator>
check_list_nodes(List & il,int value_step)7851c0b2f7Stbbdev void check_list_nodes( List& il, int value_step ) {
7951c0b2f7Stbbdev REQUIRE_MESSAGE(il.size() == unsigned(NumElements / value_step), "Wrong size of the list");
8051c0b2f7Stbbdev REQUIRE_MESSAGE(!il.empty(), "Incorrect result of empty() or the list is corrupted");
8151c0b2f7Stbbdev
8251c0b2f7Stbbdev int i;
8351c0b2f7Stbbdev Iterator it = il.begin();
8451c0b2f7Stbbdev
8551c0b2f7Stbbdev Iterator it_default;
8651c0b2f7Stbbdev REQUIRE_MESSAGE(it_default != it, "Incorrect default constructed intrusive_list::iterator");
8751c0b2f7Stbbdev
8851c0b2f7Stbbdev for ( i = value_step - 1; it != il.end(); ++it, i += value_step ) {
8951c0b2f7Stbbdev REQUIRE_MESSAGE(it->Data() == i, "Unexpected node value while iterating forward");
9051c0b2f7Stbbdev REQUIRE_MESSAGE(it->m_Canary == NoliMeTangere, "Memory corruption");
9151c0b2f7Stbbdev }
9251c0b2f7Stbbdev
9351c0b2f7Stbbdev REQUIRE_MESSAGE(i == NumElements + value_step - 1, "Wrong number of list elements while iterating forward");
9451c0b2f7Stbbdev it = il.end();
9551c0b2f7Stbbdev
9651c0b2f7Stbbdev for ( i = NumElements - 1, it--; it != il.end(); --it, i -= value_step ) {
9751c0b2f7Stbbdev REQUIRE_MESSAGE(it->Data() == i, "Unexpected node value while iterating backward");
9851c0b2f7Stbbdev REQUIRE_MESSAGE(it->m_Canary == NoliMeTangere, "Memory corruption");
9951c0b2f7Stbbdev }
10051c0b2f7Stbbdev REQUIRE_MESSAGE(i == -1, "Wrong number of list elements while iterating backward");
10151c0b2f7Stbbdev }
10251c0b2f7Stbbdev
10351c0b2f7Stbbdev template <typename List, typename Item>
test_list_operations()10451c0b2f7Stbbdev void test_list_operations() {
10551c0b2f7Stbbdev using iterator = typename List::iterator;
10651c0b2f7Stbbdev
10751c0b2f7Stbbdev List il;
10851c0b2f7Stbbdev
10951c0b2f7Stbbdev for (int i = NumElements - 1; i >= 0; --i) {
11051c0b2f7Stbbdev il.push_front(*new Item(i));
11151c0b2f7Stbbdev }
11251c0b2f7Stbbdev check_list_nodes<const List, typename List::const_iterator>(il, 1);
11351c0b2f7Stbbdev iterator it = il.begin();
11451c0b2f7Stbbdev
11551c0b2f7Stbbdev for (;it != il.end(); ++it) {
11651c0b2f7Stbbdev Item& item = *it;
11751c0b2f7Stbbdev it = il.erase(it); // also advances the iterator
11851c0b2f7Stbbdev delete &item;
11951c0b2f7Stbbdev }
12051c0b2f7Stbbdev
12151c0b2f7Stbbdev check_list_nodes<List, iterator>(il, 2);
12251c0b2f7Stbbdev for (it = il.begin(); it != il.end(); ++it) {
12351c0b2f7Stbbdev Item& item = *it;
12451c0b2f7Stbbdev il.remove(*it++); // extra advance here as well
12551c0b2f7Stbbdev delete &item;
12651c0b2f7Stbbdev }
12751c0b2f7Stbbdev
12851c0b2f7Stbbdev check_list_nodes<List, iterator>(il, 4);
12951c0b2f7Stbbdev for (it = il.begin(); it != il.end();) {
13051c0b2f7Stbbdev Item& item = *it++; // the iterator advances only here
13151c0b2f7Stbbdev il.remove(item);
13251c0b2f7Stbbdev delete &item;
13351c0b2f7Stbbdev }
13451c0b2f7Stbbdev REQUIRE_MESSAGE(il.size() == 0, "The list has wrong size or not all items were removed");
13551c0b2f7Stbbdev REQUIRE_MESSAGE(il.empty(), "Incorrect result of empty() or not all items were removed");
13651c0b2f7Stbbdev }
13751c0b2f7Stbbdev
13851c0b2f7Stbbdev // TODO: tests for intrusive_list assertions were not ported
13951c0b2f7Stbbdev
14051c0b2f7Stbbdev //! \brief \ref error_guessing
14151c0b2f7Stbbdev TEST_CASE("Test tbb::detail::r1::intrusive_list operations") {
14251c0b2f7Stbbdev test_list_operations<intrusive_list1, DataItemWithInheritedNode>();
14351c0b2f7Stbbdev }
14451c0b2f7Stbbdev
14551c0b2f7Stbbdev //! \brief \ref error_guessing
14651c0b2f7Stbbdev TEST_CASE("Test tbb::detail::r1::memptr_intrusive_list operations") {
14751c0b2f7Stbbdev test_list_operations<intrusive_list2, DataItemWithMemberNodes>();
14851c0b2f7Stbbdev test_list_operations<intrusive_list3, DataItemWithMemberNodes>();
14951c0b2f7Stbbdev }
150