diff --git a/src/hotspot/share/utilities/rbTree.hpp b/src/hotspot/share/utilities/rbTree.hpp index 89f9eb256bf..c522d787466 100644 --- a/src/hotspot/share/utilities/rbTree.hpp +++ b/src/hotspot/share/utilities/rbTree.hpp @@ -374,10 +374,10 @@ public: typedef typename BaseType::Cursor Cursor; using BaseType::cursor; using BaseType::insert_at_cursor; - using BaseType::remove_at_cursor; using BaseType::next; using BaseType::prev; + void remove_at_cursor(const Cursor& node_cursor); void replace_at_cursor(RBNode* new_node, const Cursor& node_cursor); RBNode* allocate_node(const K& key); diff --git a/src/hotspot/share/utilities/rbTree.inline.hpp b/src/hotspot/share/utilities/rbTree.inline.hpp index 70d9aee7fbf..381cb916405 100644 --- a/src/hotspot/share/utilities/rbTree.inline.hpp +++ b/src/hotspot/share/utilities/rbTree.inline.hpp @@ -1103,6 +1103,15 @@ bool RBTree::copy_into(RBTree& other) const { return true; } +template +inline void RBTree::remove_at_cursor(const Cursor& node_cursor) { + precond(node_cursor.valid()); + precond(node_cursor.found()); + RBNode* old_node = node_cursor.node(); + BaseType::remove_at_cursor(node_cursor); + free_node(old_node); +} + template inline void RBTree::replace_at_cursor(RBNode* new_node, const Cursor& node_cursor) { precond(new_node != nullptr); @@ -1170,7 +1179,7 @@ inline V* RBTree::find(const K& key) const { template inline void RBTree::remove(RBNode* node) { Cursor node_cursor = cursor(node); - remove_at_cursor(node_cursor); + BaseType::remove_at_cursor(node_cursor); free_node(node); } @@ -1181,7 +1190,7 @@ inline bool RBTree::remove(const K& key) { return false; } RBNode* node = node_cursor.node(); - remove_at_cursor(node_cursor); + BaseType::remove_at_cursor(node_cursor); free_node((RBNode*)node); return true; } diff --git a/test/hotspot/gtest/utilities/test_rbtree.cpp b/test/hotspot/gtest/utilities/test_rbtree.cpp index 17df34fb07a..7eca5f54831 100644 --- a/test/hotspot/gtest/utilities/test_rbtree.cpp +++ b/test/hotspot/gtest/utilities/test_rbtree.cpp @@ -115,6 +115,16 @@ struct ArrayAllocator { using IntrusiveTreeInt = IntrusiveRBTree; using IntrusiveCursor = IntrusiveTreeInt::Cursor; + struct DestructionTracker { + static int destructed_count; + int value; + + DestructionTracker(int value) : value(value) {} + ~DestructionTracker() { destructed_count++; } + + static void reset() { destructed_count = 0; } + }; + public: void inserting_duplicates_results_in_one_value() { constexpr int up_to = 10; @@ -607,6 +617,55 @@ public: } } + void test_remove_destructs() { + using Tree = RBTreeCHeap; + using Node = RBNode; + using Cursor = Tree::Cursor; + + Tree tree; + + // Test the 3 ways of removing a single node + tree.upsert(0, DestructionTracker(0)); + tree.upsert(1, DestructionTracker(1)); + tree.upsert(2, DestructionTracker(2)); + + DestructionTracker::reset(); + + tree.remove(0); + + Node* n = tree.find_node(1); + tree.remove(n); + + Cursor remove_cursor = tree.cursor(2); + tree.remove_at_cursor(remove_cursor); + + EXPECT_EQ(3, DestructionTracker::destructed_count); + + // Test clearing the tree + constexpr int num_nodes = 10; + for (int n = 0; n < num_nodes; n++) { + tree.upsert(n, DestructionTracker(n)); + } + + DestructionTracker::reset(); + + tree.remove_all(); + EXPECT_EQ(num_nodes, DestructionTracker::destructed_count); + + // Test replacing a node + tree.upsert(0, DestructionTracker(0)); + Cursor replace_cursor = tree.cursor(0); + Node* new_node = tree.allocate_node(1, DestructionTracker(1)); + + DestructionTracker::reset(); + + tree.replace_at_cursor(new_node, replace_cursor); + EXPECT_EQ(1, DestructionTracker::destructed_count); + + tree.remove_at_cursor(replace_cursor); + EXPECT_EQ(2, DestructionTracker::destructed_count); + } + void test_cursor() { constexpr int num_nodes = 10; RBTreeInt tree; @@ -971,6 +1030,11 @@ TEST_VM_F(RBTreeTest, NodeHints) { this->test_node_hints(); } +int RBTreeTest::DestructionTracker::destructed_count = 0; +TEST_VM_F(RBTreeTest, RemoveDestructs) { + this->test_remove_destructs(); +} + TEST_VM_F(RBTreeTest, CursorFind) { this->test_cursor(); }