/* * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
// Test put_if_absent(key) (creating a default-created value) bool created = false;
V* v = rh.put_if_absent(as_K(step), &created);
ASSERT_TRUE(rh.contains(as_K(step)));
ASSERT_TRUE(created);
*v = (V)step;
// Calling this function a second time should yield the same value pointer
V* v2 = rh.put_if_absent(as_K(step), &created);
ASSERT_EQ(v, v2);
ASSERT_EQ(*v2, *v);
ASSERT_FALSE(created);
// Test put_if_absent(key, value)
v = rh.put_if_absent(as_K(step), step, &created);
ASSERT_EQ(*v, step);
ASSERT_TRUE(rh.contains(as_K(step)));
ASSERT_TRUE(created);
v2 = rh.put_if_absent(as_K(step), step, &created); // Calling this function a second time should yield the same value pointer
ASSERT_EQ(v, v2);
ASSERT_EQ(*v2, (V)step);
ASSERT_FALSE(created);
for (uintptr_t i = 0; i < num_elements; ++i) {
ASSERT_TRUE(rh.put(as_K(i), i));
}
rh.iterate(&et); if (::testing::Test::HasFailure()) { return;
}
for (uintptr_t i = num_elements; i > 0; --i) {
uintptr_t index = i - 1;
ASSERT_TRUE((rh.remove(as_K(index))));
}
rh.iterate(&et); if (::testing::Test::HasFailure()) { return;
} for (uintptr_t i = num_elements; i > 0; --i) {
uintptr_t index = i - 1;
ASSERT_FALSE(rh.remove(as_K(index)));
}
rh.iterate(&et);
// Add more entries in and then delete one. for (uintptr_t i = 10; i > 0; --i) {
uintptr_t index = i - 1;
ASSERT_TRUE(rh.put(as_K(index), index));
}
DeleterTestIter dt(5);
rh.unlink(&dt);
ASSERT_FALSE(rh.get(as_K(5)));
}
};
};
// Simple ResourceHashtable whose key is a SymbolHandle and value is an int // This test is to show that the SymbolHandle will correctly handle the refcounting // in the table. class SimpleResourceHashtableDeleteTest : public ::testing::Test { public:
ResourceHashtable<SymbolHandle, int, 107, AnyObj::C_HEAP, mtTest, SymbolHandle::compute_hash> _simple_test_table;
class SimpleDeleter : public StackObj { public: bool do_entry(SymbolHandle& key, int value) { returntrue;
}
};
};
TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_remove) {
TempNewSymbol t = SymbolTable::new_symbol("abcdefg_simple");
Symbol* s = t; int s_orig_count = s->refcount();
_simple_test_table.put(s, 55);
ASSERT_EQ(s->refcount(), s_orig_count + 1) << "refcount should be incremented in table";
// Deleting this value from a hashtable
_simple_test_table.remove(s);
ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be same as start";
}
TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_delete) {
TempNewSymbol t = SymbolTable::new_symbol("abcdefg_simple");
Symbol* s = t; int s_orig_count = s->refcount();
_simple_test_table.put(s, 66);
ASSERT_EQ(s->refcount(), s_orig_count + 1) << "refcount should be incremented in table";
// Use unlink to remove the matching (or all) values from the table.
SimpleDeleter deleter;
_simple_test_table.unlink(&deleter);
ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be same as start";
}
// More complicated ResourceHashtable with SymbolHandle in the key. Since the *same* Symbol is part // of the value, it's not necessary to manipulate the refcount of the key, but you must in the value. // Luckily SymbolHandle does this. class ResourceHashtableDeleteTest : public ::testing::Test { public: class TestValue : public CHeapObj<mtTest> {
SymbolHandle _s; public: // Never have ctors and dtors fix refcounts without copy ctors and assignment operators! // Unless it's declared and used as a CHeapObj with // NONCOPYABLE(TestValue)
// Using SymbolHandle deals with refcount manipulation so this class doesn't have to // have dtors, copy ctors and assignment operators to do so.
TestValue(Symbol* name) : _s(name) { } // Symbol* s() const { return _s; } // needed for conversion from TempNewSymbol to SymbolHandle member
};
// ResourceHashtable whose value is a *copy* of TestValue.
ResourceHashtable<Symbol*, TestValue, 107, AnyObj::C_HEAP, mtTest> _test_table;
class Deleter : public StackObj { public: bool do_entry(Symbol*& key, TestValue& value) { // Since we didn't increment the key, we shouldn't decrement it. // Calling delete on the hashtable Node which contains value will // decrement the refcount. That's actually best since the whole // entry will be gone at once. returntrue;
}
};
// ResourceHashtable whose value is a pointer to TestValue.
ResourceHashtable<Symbol*, TestValue*, 107, AnyObj::C_HEAP, mtTest> _ptr_test_table;
class PtrDeleter : public StackObj { public: bool do_entry(Symbol*& key, TestValue*& value) { // If the hashtable value is a pointer, need to delete it from here. // This will also potentially make the refcount of the Key = 0, but the // next thing that happens is that the hashtable node is deleted so this is ok. delete value; returntrue;
}
};
};
TEST_VM_F(ResourceHashtableDeleteTest, value_remove) {
TempNewSymbol s = SymbolTable::new_symbol("abcdefg"); int s_orig_count = s->refcount();
{
TestValue tv(s); // Since TestValue contains the pointer to the key, it will handle the // refcounting.
_test_table.put(s, tv);
ASSERT_EQ(s->refcount(), s_orig_count + 2) << "refcount incremented by copy";
}
ASSERT_EQ(s->refcount(), s_orig_count + 1) << "refcount incremented in table";
// Deleting this value from a hashtable calls the destructor!
_test_table.remove(s); // Removal should make the refcount be the original refcount.
ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be as we started";
}
TEST_VM_F(ResourceHashtableDeleteTest, value_delete) {
TempNewSymbol d = SymbolTable::new_symbol("defghijklmnop"); int d_orig_count = d->refcount();
{
TestValue tv(d); // Same as above, but the do_entry does nothing because the value is deleted when the // hashtable node is deleted.
_test_table.put(d, tv);
ASSERT_EQ(d->refcount(), d_orig_count + 2) << "refcount incremented by copy";
}
ASSERT_EQ(d->refcount(), d_orig_count + 1) << "refcount incremented in table";
Deleter deleter;
_test_table.unlink(&deleter);
ASSERT_EQ(d->refcount(), d_orig_count) << "refcount should be as we started";
}
TEST_VM_F(ResourceHashtableDeleteTest, check_delete_ptr) {
TempNewSymbol s = SymbolTable::new_symbol("abcdefg_ptr"); int s_orig_count = s->refcount();
{
TestValue* tv = new TestValue(s); // Again since TestValue contains the pointer to the key Symbol, it will // handle the refcounting.
_ptr_test_table.put(s, tv);
ASSERT_EQ(s->refcount(), s_orig_count + 1) << "refcount incremented by allocation";
}
ASSERT_EQ(s->refcount(), s_orig_count + 1) << "refcount incremented in table";
// Deleting this pointer value from a hashtable must call the destructor in the // do_entry function.
PtrDeleter deleter;
_ptr_test_table.unlink(&deleter); // Removal should make the refcount be the original refcount.
ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be as we started";
}
class ResourceHashtablePrintTest : public ::testing::Test { public: class TestValue { int _i; int _j; int _k; public:
TestValue(int i) : _i(i), _j(i+1), _k(i+2) {}
};
ResourceHashtable<int, TestValue*, 30, AnyObj::C_HEAP, mtTest> _test_table;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.