Skip to content

Commit ac0e5f3

Browse files
tglsfdcCommitfest Bot
authored andcommitted
Silence complaints about leaked dynahash storage.
Because dynahash.c never frees hashtable storage except by deleting the whole hashtable context, it doesn't bother to track the individual blocks of elements allocated by element_alloc(). This results in "possibly lost" complaints from Valgrind except when the first element of each block is actively in use. (Otherwise it'll be on a freelist, but very likely only reachable via "interior pointers" within element blocks, which doesn't satisfy Valgrind.) To fix, if we're building with USE_VALGRIND, expend an extra pointer's worth of space in each element block so that we can chain them all together from the HTAB header. Skip this in shared hashtables though: Valgrind doesn't track those, and we'd need additional locking to make it safe to manipulate a shared chain. While here, update a comment obsoleted by 9c911ec. Author: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected]
1 parent a463b03 commit ac0e5f3

File tree

1 file changed

+46
-6
lines changed

1 file changed

+46
-6
lines changed

src/backend/utils/hash/dynahash.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
* lookup key's hash value as a partition number --- this will work because
2323
* of the way calc_bucket() maps hash values to bucket numbers.
2424
*
25-
* For hash tables in shared memory, the memory allocator function should
26-
* match malloc's semantics of returning NULL on failure. For hash tables
27-
* in local memory, we typically use palloc() which will throw error on
28-
* failure. The code in this file has to cope with both cases.
25+
* The memory allocator function should match malloc's semantics of returning
26+
* NULL on failure. (This is essential for hash tables in shared memory.
27+
* For hash tables in local memory, we used to use palloc() which will throw
28+
* error on failure; but we no longer do, so it's untested whether this
29+
* module will still cope with that behavior.)
2930
*
3031
* dynahash.c provides support for these types of lookup keys:
3132
*
@@ -98,6 +99,7 @@
9899

99100
#include "access/xact.h"
100101
#include "common/hashfn.h"
102+
#include "lib/ilist.h"
101103
#include "port/pg_bitutils.h"
102104
#include "storage/shmem.h"
103105
#include "storage/spin.h"
@@ -236,6 +238,16 @@ struct HTAB
236238
Size keysize; /* hash key length in bytes */
237239
long ssize; /* segment size --- must be power of 2 */
238240
int sshift; /* segment shift = log2(ssize) */
241+
242+
/*
243+
* In a USE_VALGRIND build, non-shared hashtables keep an slist chain of
244+
* all the element blocks they have allocated. This pacifies Valgrind,
245+
* which would otherwise often claim that the element blocks are "possibly
246+
* lost" for lack of any non-interior pointers to their starts.
247+
*/
248+
#ifdef USE_VALGRIND
249+
slist_head element_blocks;
250+
#endif
239251
};
240252

241253
/*
@@ -1712,6 +1724,8 @@ element_alloc(HTAB *hashp, int nelem, int freelist_idx)
17121724
{
17131725
HASHHDR *hctl = hashp->hctl;
17141726
Size elementSize;
1727+
Size requestSize;
1728+
char *allocedBlock;
17151729
HASHELEMENT *firstElement;
17161730
HASHELEMENT *tmpElement;
17171731
HASHELEMENT *prevElement;
@@ -1723,12 +1737,38 @@ element_alloc(HTAB *hashp, int nelem, int freelist_idx)
17231737
/* Each element has a HASHELEMENT header plus user data. */
17241738
elementSize = MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(hctl->entrysize);
17251739

1740+
requestSize = nelem * elementSize;
1741+
1742+
/* Add space for slist_node list link if we need one. */
1743+
#ifdef USE_VALGRIND
1744+
if (!hashp->isshared)
1745+
requestSize += MAXALIGN(sizeof(slist_node));
1746+
#endif
1747+
1748+
/* Allocate the memory. */
17261749
CurrentDynaHashCxt = hashp->hcxt;
1727-
firstElement = (HASHELEMENT *) hashp->alloc(nelem * elementSize);
1750+
allocedBlock = hashp->alloc(requestSize);
17281751

1729-
if (!firstElement)
1752+
if (!allocedBlock)
17301753
return false;
17311754

1755+
/*
1756+
* If USE_VALGRIND, each allocated block of elements of a non-shared
1757+
* hashtable is chained into a list, so that Valgrind won't think it's
1758+
* been leaked.
1759+
*/
1760+
#ifdef USE_VALGRIND
1761+
if (hashp->isshared)
1762+
firstElement = (HASHELEMENT *) allocedBlock;
1763+
else
1764+
{
1765+
slist_push_head(&hashp->element_blocks, (slist_node *) allocedBlock);
1766+
firstElement = (HASHELEMENT *) (allocedBlock + MAXALIGN(sizeof(slist_node)));
1767+
}
1768+
#else
1769+
firstElement = (HASHELEMENT *) allocedBlock;
1770+
#endif
1771+
17321772
/* prepare to link all the new entries into the freelist */
17331773
prevElement = NULL;
17341774
tmpElement = firstElement;

0 commit comments

Comments
 (0)