Skip to content

Commit cf19deb

Browse files
Fix hashtable memory leak and kvstore overhead_hashtable_lut assert (#1750)
When releasing a kvstore and its hashtables, if it is still rehashed and table 0 has no data, it may still have allocated buckets that were not released. This lead to allocated hashtable buckets being leaked. This looked like missed statistics but it was actually a memory leak. fix: #1657 --------- Signed-off-by: artikell <[email protected]> Co-authored-by: Viktor Söderqvist <[email protected]>
1 parent f85c933 commit cf19deb

File tree

3 files changed

+53
-26
lines changed

3 files changed

+53
-26
lines changed

src/hashtable.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,7 @@ void hashtableEmpty(hashtable *ht, void(callback)(hashtable *)) {
10321032
if (ht->bucket_exp[table_index] < 0) {
10331033
continue;
10341034
}
1035-
if (ht->used[table_index] > 0) {
1035+
if (ht->used[table_index] > 0 || ht->child_buckets[table_index] > 0) {
10361036
for (size_t idx = 0; idx < numBuckets(ht->bucket_exp[table_index]); idx++) {
10371037
if (callback && (idx & 65535) == 0) callback(ht);
10381038
bucket *b = &ht->tables[table_index][idx];
@@ -1058,6 +1058,7 @@ void hashtableEmpty(hashtable *ht, void(callback)(hashtable *)) {
10581058
} while (b != NULL);
10591059
}
10601060
}
1061+
10611062
zfree(ht->tables[table_index]);
10621063
if (ht->type->trackMemUsage) {
10631064
ht->type->trackMemUsage(ht, -sizeof(bucket) * numBuckets(ht->bucket_exp[table_index]));

src/kvstore.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,7 @@ void kvstoreRelease(kvstore *kvs) {
319319
if (metadata->rehashing_node) metadata->rehashing_node = NULL;
320320
hashtableRelease(ht);
321321
}
322-
/* TODO: The assert below causes a flaky unit test. Find out why. */
323-
/* assert(kvs->overhead_hashtable_lut == 0); */
322+
assert(kvs->overhead_hashtable_lut == 0);
324323
zfree(kvs->hashtables);
325324

326325
listRelease(kvs->rehashing);

src/unit/test_kvstore.c

+50-23
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ uint64_t hashTestCallback(const void *key) {
55
return hashtableGenHashFunction((char *)key, strlen((char *)key));
66
}
77

8+
uint64_t hashConflictTestCallback(const void *key) {
9+
UNUSED(key);
10+
return 0;
11+
}
12+
813
int cmpTestCallback(const void *k1, const void *k2) {
914
return strcmp(k1, k2);
1015
}
@@ -23,6 +28,16 @@ hashtableType KvstoreHashtableTestType = {
2328
.getMetadataSize = kvstoreHashtableMetadataSize,
2429
};
2530

31+
hashtableType KvstoreConflictHashtableTestType = {
32+
.hashFunction = hashConflictTestCallback,
33+
.keyCompare = cmpTestCallback,
34+
.entryDestructor = freeTestCallback,
35+
.rehashingStarted = kvstoreHashtableRehashingStarted,
36+
.rehashingCompleted = kvstoreHashtableRehashingCompleted,
37+
.trackMemUsage = kvstoreHashtableTrackMemUsage,
38+
.getMetadataSize = kvstoreHashtableMetadataSize,
39+
};
40+
2641
char *stringFromInt(int value) {
2742
char buf[32];
2843
int len;
@@ -70,31 +85,43 @@ int test_kvstoreIteratorRemoveAllKeysNoDeleteEmptyHashtable(int argc, char **arg
7085
UNUSED(argv);
7186
UNUSED(flags);
7287

73-
int i;
74-
void *key;
75-
kvstoreIterator *kvs_it;
76-
77-
int didx = 0;
78-
int curr_slot = 0;
79-
kvstore *kvs1 = kvstoreCreate(&KvstoreHashtableTestType, 0, KVSTORE_ALLOCATE_HASHTABLES_ON_DEMAND);
80-
81-
for (i = 0; i < 16; i++) {
82-
TEST_ASSERT(kvstoreHashtableAdd(kvs1, didx, stringFromInt(i)));
83-
}
84-
85-
kvs_it = kvstoreIteratorInit(kvs1, HASHTABLE_ITER_SAFE);
86-
while (kvstoreIteratorNext(kvs_it, &key)) {
87-
curr_slot = kvstoreIteratorGetCurrentHashtableIndex(kvs_it);
88-
TEST_ASSERT(kvstoreHashtableDelete(kvs1, curr_slot, key));
88+
hashtableType *type[] = {
89+
&KvstoreHashtableTestType,
90+
&KvstoreConflictHashtableTestType,
91+
NULL,
92+
};
93+
94+
for (int t = 0; type[t] != NULL; t++) {
95+
hashtableType *testType = type[t];
96+
TEST_PRINT_INFO("Testing %d hashtableType\n", t);
97+
98+
int i;
99+
void *key;
100+
kvstoreIterator *kvs_it;
101+
102+
int didx = 0;
103+
int curr_slot = 0;
104+
kvstore *kvs1 = kvstoreCreate(testType, 0, KVSTORE_ALLOCATE_HASHTABLES_ON_DEMAND);
105+
106+
for (i = 0; i < 16; i++) {
107+
TEST_ASSERT(kvstoreHashtableAdd(kvs1, didx, stringFromInt(i)));
108+
}
109+
110+
kvs_it = kvstoreIteratorInit(kvs1, HASHTABLE_ITER_SAFE);
111+
while (kvstoreIteratorNext(kvs_it, &key)) {
112+
curr_slot = kvstoreIteratorGetCurrentHashtableIndex(kvs_it);
113+
TEST_ASSERT(kvstoreHashtableDelete(kvs1, curr_slot, key));
114+
}
115+
kvstoreIteratorRelease(kvs_it);
116+
117+
hashtable *ht = kvstoreGetHashtable(kvs1, didx);
118+
TEST_ASSERT(ht != NULL);
119+
TEST_ASSERT(kvstoreHashtableSize(kvs1, didx) == 0);
120+
TEST_ASSERT(kvstoreSize(kvs1) == 0);
121+
122+
kvstoreRelease(kvs1);
89123
}
90-
kvstoreIteratorRelease(kvs_it);
91-
92-
hashtable *ht = kvstoreGetHashtable(kvs1, didx);
93-
TEST_ASSERT(ht != NULL);
94-
TEST_ASSERT(kvstoreHashtableSize(kvs1, didx) == 0);
95-
TEST_ASSERT(kvstoreSize(kvs1) == 0);
96124

97-
kvstoreRelease(kvs1);
98125
return 0;
99126
}
100127

0 commit comments

Comments
 (0)