From 62d7a2a841494c12d1814eae5ab5788dfeb9d5c1 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Tue, 7 Apr 2026 20:59:36 +0200
Subject: [PATCH v20260407 3/4] Various adjustments to resizable shmem
 accounting

Instead of continuously falling back onto .size when .maximum_size
is 0, update the copied(!) options' .maximum_size with the value
of .size.  This saves a compare operation per SHMEM_REQUEST_SPACE_SIZE()
evaluation, and simplifies which field to access for bound checks.

Similarly, .minimum_size is updated with an input of 0 meaning to
default to .size. A substitute Zero value of SHMEM_RESIZE_TO_ZERO
is used to allow users to resize the segment's memory usage to zero
bytes used.

It also reorders and deduplicates some error condition checks.
---
 src/backend/storage/ipc/shmem.c | 111 ++++++++++++++++++--------------
 src/include/storage/shmem.h     |  20 +++---
 2 files changed, 75 insertions(+), 56 deletions(-)

diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 61808c7a8e5..12de6344cac 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -108,14 +108,19 @@
  * In order to allocate resizable shared memory structures, set
  * ShmemRequestStructOpts::maximum_size to the maximum size that the structure
  * can grow to.  The address space for the maximum size will be reserved at
- * startup, but memory is allocated or freed as the structure grows or shrinks
- * respectively. ShmemRequestStructOpts::size should be set to the initial size
- * of the structure, which is the amount of memory allocated at the startup.
- * Optionally, ShmemRequestStructOpts::minimum_size can be set to the minimum
- * size that the structure can shrink to. After startup, the structure can be
- * resized by calling ShmemResizeStruct() by passing it the ShmemStructDesc for
- * the structure and the new size. ShmemResizeStruct() enforces that the new
- * size is within [minimum_size, maximum_size].
+ * startup, whilst the backing memory is allocated or freed as the structure
+ * grows or shrinks respectively.  ShmemRequestStructOpts::size should be set
+ * to the initial size of the structure at startup.  Optionally,
+ * ShmemRequestStructOpts::minimum_size can be set to the minimum size that
+ * the structure can shrink to.  A sentinel value of SHMEM_RESIZE_TO_ZERO can
+ * be used used to indicate the struct can scale its memory usage down to 0
+ * bytes; the natural 0 is assumed to be uninitialized and so will cause the
+ * minimum size to default to the value of ShmemRequestStructOpts::size, like
+ * ShmemRequestStructOpts::maximum_size.
+ * After startup, the structure can be resized by calling ShmemResizeStruct()
+ * by passing it the ShmemStructDesc for the structure and the new size.
+ * ShmemResizeStruct() enforces that the new size is within
+ * [minimum_size, maximum_size].
  *
  * While resizable structures can be created after the startup, the memory
  * available for them is quite limited.
@@ -192,8 +197,7 @@ typedef struct
  * resizable shmem, the maximum_size is ensured to be 0 i.e. all the structures
  * are treated as fixed-size structures.
  */
-#define SHMEM_REQUEST_SPACE_SIZE(request) \
-	((request)->options->maximum_size > 0 ? (request)->options->maximum_size : (request)->options->size)
+#define SHMEM_REQUEST_SPACE_SIZE(request) ((request)->options->maximum_size)
 
 static List *pending_shmem_requests;
 
@@ -371,70 +375,81 @@ ShmemRequestInternal(ShmemStructOpts *options, ShmemRequestKind kind)
 {
 	ShmemRequest *request;
 
+	/* Check that we're in the right state */
+	if (shmem_request_state != SRS_REQUESTING)
+		elog(ERROR, "ShmemRequestStruct can only be called from a shmem_request callback");
+
 	/* Check the options */
 	if (options->name == NULL)
 		elog(ERROR, "shared memory request is missing 'name' option");
 
+	/*
+	 * Sanitize the options input by populating min/max with their actual values.
+	 * 
+	 * Note that minimum_size's zero-initialized value "not specified" conflicts
+	 * with a natural value for "resize to zero", so "resize to zero" has its own
+	 * sentinel value with SHMEM_RESIZE_TO_ZERO.
+	 */
+	if (options->maximum_size == 0)
+	{
+		options->maximum_size = options->size;
+		Assert(options->minimum_size == 0);
+	}
+
+	if (options->minimum_size == 0)
+		options->minimum_size = options->size;
+	else if (options->minimum_size == SHMEM_RESIZE_TO_ZERO)
+		options->minimum_size = 0;
+
+	/* resizing shmem segment */
+	if (options->maximum_size != options->minimum_size)
+	{
 #ifndef HAVE_RESIZABLE_SHMEM
-	if (options->maximum_size > 0)
 		elog(ERROR, "resizable shared memory is not supported on this platform");
 #else
-	if (options->maximum_size > 0 && shared_memory_type != SHMEM_TYPE_MMAP)
-		elog(ERROR, "resizable shared memory requires shared_memory_type = mmap");
+		if (shared_memory_type != SHMEM_TYPE_MMAP)
+			elog(ERROR, "resizable shared memory requires shared_memory_type = mmap");
 #endif
-
-	if (IsUnderPostmaster)
-	{
-		if (options->size <= 0 && options->size != SHMEM_ATTACH_UNKNOWN_SIZE)
-			elog(ERROR, "invalid size %zd for shared memory request for \"%s\"",
-				 options->size, options->name);
-		if (options->minimum_size < 0 && options->minimum_size != SHMEM_ATTACH_UNKNOWN_SIZE)
-			elog(ERROR, "invalid minimum_size %zd for shared memory request for \"%s\"",
-				 options->minimum_size, options->name);
-		if (options->maximum_size < 0 && options->maximum_size != SHMEM_ATTACH_UNKNOWN_SIZE)
-			elog(ERROR, "invalid maximum_size %zd for shared memory request for \"%s\"",
-				 options->maximum_size, options->name);
 	}
-	else
+
+	if (!IsUnderPostmaster)
 	{
 		if (options->size == SHMEM_ATTACH_UNKNOWN_SIZE)
 			elog(ERROR, "SHMEM_ATTACH_UNKNOWN_SIZE cannot be used during startup");
-		if (options->size <= 0)
-			elog(ERROR, "invalid size %zd for shared memory request for \"%s\"",
-				 options->size, options->name);
 		if (options->minimum_size == SHMEM_ATTACH_UNKNOWN_SIZE)
 			elog(ERROR, "SHMEM_ATTACH_UNKNOWN_SIZE cannot be used during startup");
-		if (options->minimum_size < 0)
-			elog(ERROR, "invalid minimum_size %zd for shared memory request for \"%s\"",
-				 options->minimum_size, options->name);
 		if (options->maximum_size == SHMEM_ATTACH_UNKNOWN_SIZE)
 			elog(ERROR, "SHMEM_ATTACH_UNKNOWN_SIZE cannot be used during startup");
-		if (options->maximum_size < 0)
-			elog(ERROR, "invalid maximum_size %zd for shared memory request for \"%s\"",
-				 options->maximum_size, options->name);
 	}
 
+	if (options->size <= 0 && options->size != SHMEM_ATTACH_UNKNOWN_SIZE)
+		elog(ERROR, "invalid size %zd for shared memory request for \"%s\"",
+			 options->size, options->name);
+	if (options->minimum_size < 0 && options->minimum_size != SHMEM_ATTACH_UNKNOWN_SIZE)
+		elog(ERROR, "invalid minimum_size %zd for shared memory request for \"%s\"",
+			 options->minimum_size, options->name);
+	if (options->maximum_size < 0 && options->maximum_size != SHMEM_ATTACH_UNKNOWN_SIZE)
+		elog(ERROR, "invalid maximum_size %zd for shared memory request for \"%s\"",
+			 options->maximum_size, options->name);
+
 	if (options->alignment != 0 && pg_nextpower2_size_t(options->alignment) != options->alignment)
 		elog(ERROR, "invalid alignment %zu for shared memory request for \"%s\"",
 			 options->alignment, options->name);
 
-	if (options->minimum_size > 0 && options->size != SHMEM_ATTACH_UNKNOWN_SIZE &&
-		options->minimum_size > options->size)
-		elog(ERROR, "resizable shared memory structure \"%s\" should have minimum size (%zd) less than or equal to size (%zd)",
-			 options->name, options->minimum_size, options->size);
-
-	if (options->maximum_size > 0 && options->size > options->maximum_size)
-		elog(ERROR, "resizable shared memory structure \"%s\" should have maximum size (%zd) greater than size (%zd)",
-			 options->name, options->maximum_size, options->size);
-
-	if (options->minimum_size > 0 && options->maximum_size > 0 &&
+	if (options->minimum_size != SHMEM_ATTACH_UNKNOWN_SIZE && options->maximum_size != SHMEM_ATTACH_UNKNOWN_SIZE &&
 		options->minimum_size > options->maximum_size)
 		elog(ERROR, "resizable shared memory structure \"%s\" should have minimum size (%zd) less than or equal to maximum size (%zd)",
 			 options->name, options->minimum_size, options->maximum_size);
 
-	/* Check that we're in the right state */
-	if (shmem_request_state != SRS_REQUESTING)
-		elog(ERROR, "ShmemRequestStruct can only be called from a shmem_request callback");
+	if (options->minimum_size != SHMEM_ATTACH_UNKNOWN_SIZE && options->size != SHMEM_ATTACH_UNKNOWN_SIZE &&
+		options->minimum_size > options->size)
+		elog(ERROR, "resizable shared memory structure \"%s\" should have minimum size (%zd) less than or equal to size (%zd)",
+			 options->name, options->minimum_size, options->size);
+
+	if (options->maximum_size != SHMEM_ATTACH_UNKNOWN_SIZE && options->size != SHMEM_ATTACH_UNKNOWN_SIZE &&
+		options->size > options->maximum_size)
+		elog(ERROR, "resizable shared memory structure \"%s\" should have size (%zd) less than or equal to maximum size (%zd)",
+			 options->name, options->size, options->maximum_size);
 
 	/* Check that it's not already registered in this process */
 	foreach_ptr(ShmemRequest, existing, pending_shmem_requests)
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index f8ddb0dd7c0..2682704b3ef 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -58,18 +58,21 @@ typedef struct ShmemStructOpts
 	size_t		alignment;
 
 	/*
-	 * Minimum size this structure can shrink to. Should be set to 0 for
-	 * fixed-size structures.
+	 * Minimum size this structure can shrink to.
+	 * When initialized to 0, it defaults to the value of the .size field; use
+	 * SHMEM_RESIZE_TO_ZERO instead if you want your shmem allocation to be
+	 * able to shrink to 'no memory usage'.
 	 */
 	ssize_t		minimum_size;
 
 	/*
-	 * Maximum size this structure can grow upto in future. The memory is not
-	 * allocated right away but the corresponding address space is reserved so
-	 * that memory can be mapped to it when the structure grows. Typically
-	 * should be used for large resizable structures which need several pages
-	 * worth of contiguous memory. Should be set to 0 for fixed-size
-	 * structures.
+	 * Maximum size this structure can grow up to in the future. The memory not
+	 * required for .size is not allocated right away, but the corresponding
+	 * address space is reserved so that memory can be mapped to it when the
+	 * structure grows. Typically, this should be used for large resizable
+	 * structures which need several pages worth of contiguous memory.
+	 *
+	 * When set to 0, it defaults to the value of the .size field.
 	 */
 	ssize_t		maximum_size;
 
@@ -83,6 +86,7 @@ typedef struct ShmemStructOpts
 } ShmemStructOpts;
 
 #define SHMEM_ATTACH_UNKNOWN_SIZE (-1)
+#define SHMEM_RESIZE_TO_ZERO (-2)
 
 /*
  * Options for ShmemRequestHash()
-- 
2.50.1 (Apple Git-155)

