Merge pull request #38828 from RandomShaper/fix_oa_hash_map_3.2

Fix leaks and crashes in OAHashMap (3.2)
This commit is contained in:
Rémi Verschelde 2020-05-18 15:04:31 +02:00 committed by GitHub
commit 8e91d2746d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 92 additions and 16 deletions

View File

@ -45,6 +45,9 @@
* *
* The entries are stored inplace, so huge keys or values might fill cache lines * The entries are stored inplace, so huge keys or values might fill cache lines
* a lot faster. * a lot faster.
*
* Only used keys and values are constructed. For free positions there's space
* in the arrays for each, but that memory is kept uninitialized.
*/ */
template <class TKey, class TValue, template <class TKey, class TValue,
class Hasher = HashMapHasherDefault, class Hasher = HashMapHasherDefault,
@ -149,9 +152,9 @@ private:
uint32_t *old_hashes = hashes; uint32_t *old_hashes = hashes;
num_elements = 0; num_elements = 0;
keys = memnew_arr(TKey, capacity); keys = static_cast<TKey *>(Memory::alloc_static(sizeof(TKey) * capacity));
values = memnew_arr(TValue, capacity); values = static_cast<TValue *>(Memory::alloc_static(sizeof(TValue) * capacity));
hashes = memnew_arr(uint32_t, capacity); hashes = static_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
for (uint32_t i = 0; i < capacity; i++) { for (uint32_t i = 0; i < capacity; i++) {
hashes[i] = 0; hashes[i] = 0;
@ -163,11 +166,14 @@ private:
} }
_insert_with_hash(old_hashes[i], old_keys[i], old_values[i]); _insert_with_hash(old_hashes[i], old_keys[i], old_values[i]);
old_keys[i].~TKey();
old_values[i].~TValue();
} }
memdelete_arr(old_keys); Memory::free_static(old_keys);
memdelete_arr(old_values); Memory::free_static(old_values);
memdelete_arr(old_hashes); Memory::free_static(old_hashes);
} }
void _resize_and_rehash() { void _resize_and_rehash() {
@ -214,8 +220,7 @@ public:
bool exists = _lookup_pos(p_key, pos); bool exists = _lookup_pos(p_key, pos);
if (exists) { if (exists) {
values[pos].~TValue(); values[pos] = p_data;
memnew_placement(&values[pos], TValue(p_data));
} else { } else {
insert(p_key, p_data); insert(p_key, p_data);
} }
@ -232,8 +237,7 @@ public:
bool exists = _lookup_pos(p_key, pos); bool exists = _lookup_pos(p_key, pos);
if (exists) { if (exists) {
r_data.~TValue(); r_data = values[pos];
memnew_placement(&r_data, TValue(values[pos]));
return true; return true;
} }
@ -336,9 +340,9 @@ public:
capacity = p_initial_capacity; capacity = p_initial_capacity;
num_elements = 0; num_elements = 0;
keys = memnew_arr(TKey, p_initial_capacity); keys = static_cast<TKey *>(Memory::alloc_static(sizeof(TKey) * capacity));
values = memnew_arr(TValue, p_initial_capacity); values = static_cast<TValue *>(Memory::alloc_static(sizeof(TValue) * capacity));
hashes = memnew_arr(uint32_t, p_initial_capacity); hashes = static_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
for (uint32_t i = 0; i < p_initial_capacity; i++) { for (uint32_t i = 0; i < p_initial_capacity; i++) {
hashes[i] = EMPTY_HASH; hashes[i] = EMPTY_HASH;
@ -347,9 +351,19 @@ public:
~OAHashMap() { ~OAHashMap() {
memdelete_arr(keys); for (uint32_t i = 0; i < capacity; i++) {
memdelete_arr(values);
memdelete_arr(hashes); if (hashes[i] == EMPTY_HASH) {
continue;
}
values[i].~TValue();
keys[i].~TKey();
}
Memory::free_static(keys);
Memory::free_static(values);
Memory::free_static(hashes);
} }
}; };

View File

@ -35,6 +35,41 @@
namespace TestOAHashMap { namespace TestOAHashMap {
struct CountedItem {
static int count;
int id;
bool destroyed;
CountedItem() :
id(-1),
destroyed(false) {
count++;
}
CountedItem(int p_id) :
id(p_id),
destroyed(false) {
count++;
}
CountedItem(const CountedItem &p_other) :
id(p_other.id),
destroyed(false) {
count++;
}
CountedItem &operator=(const CountedItem &p_other) = default;
~CountedItem() {
CRASH_COND(destroyed);
count--;
destroyed = true;
}
};
int CountedItem::count;
MainLoop *test() { MainLoop *test() {
OS::get_singleton()->print("\n\n\nHello from test\n"); OS::get_singleton()->print("\n\n\nHello from test\n");
@ -152,6 +187,33 @@ MainLoop *test() {
map.set(5, 1); map.set(5, 1);
} }
// test memory management of items, should not crash or leak items
{
// Exercise different patterns of removal
for (int i = 0; i < 4; ++i) {
{
OAHashMap<String, CountedItem> map;
int id = 0;
for (int j = 0; j < 100; ++j) {
map.insert(itos(j), CountedItem(id));
}
if (i <= 1) {
for (int j = 0; j < 100; ++j) {
map.remove(itos(j));
}
}
if (i % 2 == 0) {
map.clear();
}
}
if (CountedItem::count != 0) {
OS::get_singleton()->print("%d != 0 (not performing the other test sub-cases, breaking...)\n", CountedItem::count);
break;
}
}
}
return NULL; return NULL;
} }
} // namespace TestOAHashMap } // namespace TestOAHashMap