Skip to content

Commit b282fa8

Browse files
committed
simplehash: preserve consistency in case of OOM.
Compute size first, then allocate, then update the structure. Previously, an out-of-memory when growing could leave the hashtable in an inconsistent state. Discussion: https://postgr.es/m/20231117201334.eyb542qr5yk4gilq@awork3.anarazel.de Reviewed-by: Andres Freund Reviewed-by: Gurjeet Singh
1 parent a268a51 commit b282fa8

File tree

1 file changed

+31
-11
lines changed

1 file changed

+31
-11
lines changed

src/include/lib/simplehash.h

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@
128128
#define SH_STAT SH_MAKE_NAME(stat)
129129

130130
/* internal helper functions (no externally visible prototypes) */
131-
#define SH_COMPUTE_PARAMETERS SH_MAKE_NAME(compute_parameters)
131+
#define SH_COMPUTE_SIZE SH_MAKE_NAME(compute_size)
132+
#define SH_UPDATE_PARAMETERS SH_MAKE_NAME(update_parameters)
132133
#define SH_NEXT SH_MAKE_NAME(next)
133134
#define SH_PREV SH_MAKE_NAME(prev)
134135
#define SH_DISTANCE_FROM_OPTIMAL SH_MAKE_NAME(distance)
@@ -303,11 +304,11 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
303304
#endif
304305

305306
/*
306-
* Compute sizing parameters for hashtable. Called when creating and growing
307-
* the hashtable.
307+
* Compute allocation size for hashtable. Result can be passed to
308+
* SH_UPDATE_PARAMETERS.
308309
*/
309-
static inline void
310-
SH_COMPUTE_PARAMETERS(SH_TYPE * tb, uint64 newsize)
310+
static inline uint64
311+
SH_COMPUTE_SIZE(uint64 newsize)
311312
{
312313
uint64 size;
313314

@@ -325,6 +326,18 @@ SH_COMPUTE_PARAMETERS(SH_TYPE * tb, uint64 newsize)
325326
if (unlikely((((uint64) sizeof(SH_ELEMENT_TYPE)) * size) >= SIZE_MAX / 2))
326327
sh_error("hash table too large");
327328

329+
return size;
330+
}
331+
332+
/*
333+
* Update sizing parameters for hashtable. Called when creating and growing
334+
* the hashtable.
335+
*/
336+
static inline void
337+
SH_UPDATE_PARAMETERS(SH_TYPE * tb, uint64 newsize)
338+
{
339+
uint64 size = SH_COMPUTE_SIZE(newsize);
340+
328341
/* now set size */
329342
tb->size = size;
330343
tb->sizemask = (uint32) (size - 1);
@@ -446,10 +459,11 @@ SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data)
446459
/* increase nelements by fillfactor, want to store nelements elements */
447460
size = Min((double) SH_MAX_SIZE, ((double) nelements) / SH_FILLFACTOR);
448461

449-
SH_COMPUTE_PARAMETERS(tb, size);
462+
size = SH_COMPUTE_SIZE(size);
450463

451-
tb->data = (SH_ELEMENT_TYPE *) SH_ALLOCATE(tb, sizeof(SH_ELEMENT_TYPE) * tb->size);
464+
tb->data = (SH_ELEMENT_TYPE *) SH_ALLOCATE(tb, sizeof(SH_ELEMENT_TYPE) * size);
452465

466+
SH_UPDATE_PARAMETERS(tb, size);
453467
return tb;
454468
}
455469

@@ -490,10 +504,15 @@ SH_GROW(SH_TYPE * tb, uint64 newsize)
490504
Assert(oldsize != SH_MAX_SIZE);
491505
Assert(oldsize < newsize);
492506

493-
/* compute parameters for new table */
494-
SH_COMPUTE_PARAMETERS(tb, newsize);
507+
newsize = SH_COMPUTE_SIZE(newsize);
495508

496-
tb->data = (SH_ELEMENT_TYPE *) SH_ALLOCATE(tb, sizeof(SH_ELEMENT_TYPE) * tb->size);
509+
tb->data = (SH_ELEMENT_TYPE *) SH_ALLOCATE(tb, sizeof(SH_ELEMENT_TYPE) * newsize);
510+
511+
/*
512+
* Update parameters for new table after allocation succeeds to avoid
513+
* inconsistent state on OOM.
514+
*/
515+
SH_UPDATE_PARAMETERS(tb, newsize);
497516

498517
newdata = tb->data;
499518

@@ -1173,7 +1192,8 @@ SH_STAT(SH_TYPE * tb)
11731192
#undef SH_STAT
11741193

11751194
/* internal function names */
1176-
#undef SH_COMPUTE_PARAMETERS
1195+
#undef SH_COMPUTE_SIZE
1196+
#undef SH_UPDATE_PARAMETERS
11771197
#undef SH_COMPARE_KEYS
11781198
#undef SH_INITIAL_BUCKET
11791199
#undef SH_NEXT

0 commit comments

Comments
 (0)