(moving to pgsql-hackers)

On 29/01/2024 14:06, Heikki Linnakangas wrote:
If you call dsa_allocate_extended(DSA_ALLOC_NO_OOM), it will still
ereport an error if you run out of space (originally reported at [0]).

Attached patch adds code to test_dsa.c to demonstrate that:

postgres=# select test_dsa_basic();
ERROR:  could not resize shared memory segment "/PostgreSQL.1312700148"
to 1075843072 bytes: No space left on device

[0] https://github.com/pgvector/pgvector/issues/434#issuecomment-1912744489

I wrote the attached patch to address this, in a fairly straightforward or naive way. The problem was that even though dsa_allocate() had code to check the return value of dsm_create(), and return NULL instead of ereport(ERROR) if the DSA_ALLOC_NO_OOM was set, dsm_create() does not in fact return NULL on OOM. To fix, I added a DSM_CREATE_NO_OOM option to dsm_create(), and I also had to punch that through to dsm_impl_op().

This is a little unpolished, but if we want to backpatch something narrow now, this would probably be the right approach.

However, I must say that the dsm_impl_op() interface is absolutely insane. It's like someone looked at ioctl() and thought, "hey that's a great idea!". It mixes all different operations like creating or destroying a DSM segment together into one function call, and the return value is just a boolean, even though the function could fail for many different reasons, and the callers do in fact care about the reason. In a more natural interface, the different operations would have very different function signatures.

I think we must refactor that. It might be best to leave this DSA_ALLOC_NO_OOM bug unfixed in backpatches, and fix it on top of the refactorings on master only. Later, we can backpatch the refactorings too if we're comfortable with it; extensions shouldn't be using the dsm_impl_op() interface directly.

(I skimmed through the thread where the DSM code was added, but didn't see any mention of why dsm_impl_op() signature is like that: https://www.postgresql.org/message-id/CA%2BTgmoaDqDUgt%3D4Zs_QPOnBt%3DEstEaVNP%2B5t%2Bm%3DFPNWshiPR3A%40mail.gmail.com)

--
Heikki Linnakangas
Neon (https://neon.tech)
From b60f877ad67e70b60915b2b25d0dbae6972c3536 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakan...@iki.fi>
Date: Tue, 13 Feb 2024 14:22:10 +0200
Subject: [PATCH 1/2] Add test

Discussion: https://www.postgresql.org/message-id/5efa4a5e-2b8b-42dd-80ed-f920718cf...@iki.fi
---
 src/test/modules/test_dsa/test_dsa.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/src/test/modules/test_dsa/test_dsa.c b/src/test/modules/test_dsa/test_dsa.c
index 844316dec2b..f37eb57e99d 100644
--- a/src/test/modules/test_dsa/test_dsa.c
+++ b/src/test/modules/test_dsa/test_dsa.c
@@ -51,6 +51,19 @@ test_dsa_basic(PG_FUNCTION_ARGS)
 	for (int i = 0; i < 100; i++)
 	{
 		dsa_free(a, p[i]);
+		p[i] = InvalidDsaPointer;
+	}
+
+	for (int i = 0; i < 100; i++)
+		p[i] = dsa_allocate_extended(a, 1024*1024*1024, DSA_ALLOC_NO_OOM | DSA_ALLOC_HUGE);
+
+	for (int i = 0; i < 100; i++)
+	{
+		if (p[i] != InvalidDsaPointer)
+		{
+			dsa_free(a, p[i]);
+			p[i] = InvalidDsaPointer;
+		}
 	}
 
 	dsa_detach(a);
-- 
2.39.2

From fb20d55111d726dca247b2111a175f30d412e35a Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakan...@iki.fi>
Date: Tue, 13 Feb 2024 15:52:48 +0200
Subject: [PATCH 2/2] Fix DSA_ALLOC_NO_OOM

---
 src/backend/access/common/session.c   |  2 +-
 src/backend/access/transam/parallel.c |  2 +-
 src/backend/storage/ipc/dsm.c         | 43 +++++++++-----
 src/backend/storage/ipc/dsm_impl.c    | 84 ++++++++++++++++++++-------
 src/backend/utils/mmgr/dsa.c          |  2 +-
 src/include/storage/dsm.h             |  1 +
 src/include/storage/dsm_impl.h        |  4 +-
 7 files changed, 97 insertions(+), 41 deletions(-)

diff --git a/src/backend/access/common/session.c b/src/backend/access/common/session.c
index 3f2256f4915..5d4fe6dbb7a 100644
--- a/src/backend/access/common/session.c
+++ b/src/backend/access/common/session.c
@@ -102,7 +102,7 @@ GetSessionDsmHandle(void)
 
 	/* Set up segment and TOC. */
 	size = shm_toc_estimate(&estimator);
-	seg = dsm_create(size, DSM_CREATE_NULL_IF_MAXSEGMENTS);
+	seg = dsm_create(size, DSM_CREATE_NULL_IF_MAXSEGMENTS | DSM_CREATE_NO_OOM);
 	if (seg == NULL)
 	{
 		MemoryContextSwitchTo(old_context);
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 849a03e4b65..df1e5e7145d 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -312,7 +312,7 @@ InitializeParallelDSM(ParallelContext *pcxt)
 	 */
 	segsize = shm_toc_estimate(&pcxt->estimator);
 	if (pcxt->nworkers > 0)
-		pcxt->seg = dsm_create(segsize, DSM_CREATE_NULL_IF_MAXSEGMENTS);
+		pcxt->seg = dsm_create(segsize, DSM_CREATE_NULL_IF_MAXSEGMENTS | DSM_CREATE_NO_OOM);
 	if (pcxt->seg != NULL)
 		pcxt->toc = shm_toc_create(PARALLEL_MAGIC,
 								   dsm_segment_address(pcxt->seg),
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index 6b12108dd10..2d2596fbe2d 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -214,7 +214,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
 						&dsm_control_impl_private, &dsm_control_address,
-						&dsm_control_mapped_size, ERROR))
+						&dsm_control_mapped_size, ERROR, 0))
 			break;
 	}
 	dsm_control = dsm_control_address;
@@ -255,7 +255,7 @@ dsm_cleanup_using_control_segment(dsm_handle old_control_handle)
 	 * out quietly.
 	 */
 	if (!dsm_impl_op(DSM_OP_ATTACH, old_control_handle, 0, &impl_private,
-					 &mapped_address, &mapped_size, DEBUG1))
+					 &mapped_address, &mapped_size, DEBUG1, 0))
 		return;
 
 	/*
@@ -266,7 +266,7 @@ dsm_cleanup_using_control_segment(dsm_handle old_control_handle)
 	if (!dsm_control_segment_sane(old_control, mapped_size))
 	{
 		dsm_impl_op(DSM_OP_DETACH, old_control_handle, 0, &impl_private,
-					&mapped_address, &mapped_size, LOG);
+					&mapped_address, &mapped_size, LOG, 0);
 		return;
 	}
 
@@ -296,7 +296,7 @@ dsm_cleanup_using_control_segment(dsm_handle old_control_handle)
 
 		/* Destroy the referenced segment. */
 		dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private,
-					&junk_mapped_address, &junk_mapped_size, LOG);
+					&junk_mapped_address, &junk_mapped_size, LOG, 0);
 	}
 
 	/* Destroy the old control segment, too. */
@@ -304,7 +304,7 @@ dsm_cleanup_using_control_segment(dsm_handle old_control_handle)
 		 "cleaning up dynamic shared memory control segment with ID %u",
 		 old_control_handle);
 	dsm_impl_op(DSM_OP_DESTROY, old_control_handle, 0, &impl_private,
-				&mapped_address, &mapped_size, LOG);
+				&mapped_address, &mapped_size, LOG, 0);
 }
 
 /*
@@ -400,7 +400,7 @@ dsm_postmaster_shutdown(int code, Datum arg)
 
 		/* Destroy the segment. */
 		dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private,
-					&junk_mapped_address, &junk_mapped_size, LOG);
+					&junk_mapped_address, &junk_mapped_size, LOG, 0);
 	}
 
 	/* Remove the control segment itself. */
@@ -410,7 +410,7 @@ dsm_postmaster_shutdown(int code, Datum arg)
 	dsm_control_address = dsm_control;
 	dsm_impl_op(DSM_OP_DESTROY, dsm_control_handle, 0,
 				&dsm_control_impl_private, &dsm_control_address,
-				&dsm_control_mapped_size, LOG);
+				&dsm_control_mapped_size, LOG, 0);
 	dsm_control = dsm_control_address;
 	shim->dsm_control = 0;
 }
@@ -512,6 +512,8 @@ dsm_shmem_init(void)
  * remains attached until explicitly detached or the session ends.
  * Creating with a NULL CurrentResourceOwner is equivalent to creating
  * with a non-NULL CurrentResourceOwner and then calling dsm_pin_mapping.
+ *
+ * XXX: explain flags
  */
 dsm_segment *
 dsm_create(Size size, int flags)
@@ -569,14 +571,25 @@ dsm_create(Size size, int flags)
 			LWLockRelease(DynamicSharedMemoryControlLock);
 		for (;;)
 		{
+			int			impl_flags = ((flags & DSM_CREATE_NO_OOM) != 0) ? DSM_OP_CREATE_NO_OOM : 0;
+
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
 			/* Use even numbers only */
 			seg->handle = pg_prng_uint32(&pg_global_prng_state) << 1;
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
+			errno = 0;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
-							&seg->mapped_address, &seg->mapped_size, ERROR))
+							&seg->mapped_address, &seg->mapped_size, ERROR, impl_flags))
 				break;
+			if ((flags & DSM_CREATE_NO_OOM) != 0 && (errno == ENOMEM || errno == ENOSPC))
+			{
+				if (seg->resowner != NULL)
+					ResourceOwnerForgetDSM(seg->resowner, seg);
+				dlist_delete(&seg->node);
+				pfree(seg);
+				return NULL;
+			}
 		}
 		LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
 	}
@@ -614,13 +627,13 @@ dsm_create(Size size, int flags)
 		LWLockRelease(DynamicSharedMemoryControlLock);
 		if (!using_main_dsm_region)
 			dsm_impl_op(DSM_OP_DESTROY, seg->handle, 0, &seg->impl_private,
-						&seg->mapped_address, &seg->mapped_size, WARNING);
+						&seg->mapped_address, &seg->mapped_size, WARNING, 0);
 		if (seg->resowner != NULL)
 			ResourceOwnerForgetDSM(seg->resowner, seg);
 		dlist_delete(&seg->node);
 		pfree(seg);
 
-		if ((flags & DSM_CREATE_NULL_IF_MAXSEGMENTS) != 0)
+		if ((flags & (DSM_CREATE_NULL_IF_MAXSEGMENTS | DSM_CREATE_NO_OOM)) != 0)
 			return NULL;
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
@@ -744,7 +757,7 @@ dsm_attach(dsm_handle h)
 	/* Here's where we actually try to map the segment. */
 	if (!is_main_region_dsm_handle(seg->handle))
 		dsm_impl_op(DSM_OP_ATTACH, seg->handle, 0, &seg->impl_private,
-					&seg->mapped_address, &seg->mapped_size, ERROR);
+					&seg->mapped_address, &seg->mapped_size, ERROR, 0);
 
 	return seg;
 }
@@ -788,7 +801,7 @@ dsm_detach_all(void)
 	if (control_address != NULL)
 		dsm_impl_op(DSM_OP_DETACH, dsm_control_handle, 0,
 					&dsm_control_impl_private, &control_address,
-					&dsm_control_mapped_size, ERROR);
+					&dsm_control_mapped_size, ERROR, 0);
 }
 
 /*
@@ -841,7 +854,7 @@ dsm_detach(dsm_segment *seg)
 	{
 		if (!is_main_region_dsm_handle(seg->handle))
 			dsm_impl_op(DSM_OP_DETACH, seg->handle, 0, &seg->impl_private,
-						&seg->mapped_address, &seg->mapped_size, WARNING);
+						&seg->mapped_address, &seg->mapped_size, WARNING, 0);
 		seg->impl_private = NULL;
 		seg->mapped_address = NULL;
 		seg->mapped_size = 0;
@@ -883,7 +896,7 @@ dsm_detach(dsm_segment *seg)
 			 */
 			if (is_main_region_dsm_handle(seg->handle) ||
 				dsm_impl_op(DSM_OP_DESTROY, seg->handle, 0, &seg->impl_private,
-							&seg->mapped_address, &seg->mapped_size, WARNING))
+							&seg->mapped_address, &seg->mapped_size, WARNING, 0))
 			{
 				LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
 				if (is_main_region_dsm_handle(seg->handle))
@@ -1055,7 +1068,7 @@ dsm_unpin_segment(dsm_handle handle)
 		 */
 		if (is_main_region_dsm_handle(handle) ||
 			dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private,
-						&junk_mapped_address, &junk_mapped_size, WARNING))
+						&junk_mapped_address, &junk_mapped_size, WARNING, 0))
 		{
 			LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
 			if (is_main_region_dsm_handle(handle))
diff --git a/src/backend/storage/ipc/dsm_impl.c b/src/backend/storage/ipc/dsm_impl.c
index 03aa47a1049..693caf8a64b 100644
--- a/src/backend/storage/ipc/dsm_impl.c
+++ b/src/backend/storage/ipc/dsm_impl.c
@@ -72,23 +72,23 @@
 #ifdef USE_DSM_POSIX
 static bool dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
 						   void **impl_private, void **mapped_address,
-						   Size *mapped_size, int elevel);
+						   Size *mapped_size, int elevel, int flags);
 static int	dsm_impl_posix_resize(int fd, off_t size);
 #endif
 #ifdef USE_DSM_SYSV
 static bool dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
 						  void **impl_private, void **mapped_address,
-						  Size *mapped_size, int elevel);
+						  Size *mapped_size, int elevel, int flags);
 #endif
 #ifdef USE_DSM_WINDOWS
 static bool dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size,
 							 void **impl_private, void **mapped_address,
-							 Size *mapped_size, int elevel);
+							 Size *mapped_size, int elevel, int flags);
 #endif
 #ifdef USE_DSM_MMAP
 static bool dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
 						  void **impl_private, void **mapped_address,
-						  Size *mapped_size, int elevel);
+						  Size *mapped_size, int elevel, int flags);
 #endif
 static int	errcode_for_dynamic_shared_memory(void);
 
@@ -153,38 +153,45 @@ int			min_dynamic_shared_memory;
  * a message should first be logged at the specified elevel, except in the
  * case where DSM_OP_CREATE experiences a name collision, which should
  * silently return false.
+ *
+ * If op is DSM_OP_CREATE and the DSM_OP_CREATE_NO_OOM flag is set, returns
+ * false on a failure caused by running out of memory or disk space,
+ * regardless of elevel. That can be distinguished from a name collision by
+ * checking if 'errno' is ENOSPC or ENOMEM.
  *-----
  */
 bool
 dsm_impl_op(dsm_op op, dsm_handle handle, Size request_size,
 			void **impl_private, void **mapped_address, Size *mapped_size,
-			int elevel)
+			int elevel, int flags)
 {
 	Assert(op == DSM_OP_CREATE || request_size == 0);
 	Assert((op != DSM_OP_CREATE && op != DSM_OP_ATTACH) ||
 		   (*mapped_address == NULL && *mapped_size == 0));
+	Assert((op == DSM_OP_CREATE && (flags & ~DSM_OP_CREATE_NO_OOM) == 0) ||
+		   flags == 0);
 
 	switch (dynamic_shared_memory_type)
 	{
 #ifdef USE_DSM_POSIX
 		case DSM_IMPL_POSIX:
 			return dsm_impl_posix(op, handle, request_size, impl_private,
-								  mapped_address, mapped_size, elevel);
+								  mapped_address, mapped_size, elevel, flags);
 #endif
 #ifdef USE_DSM_SYSV
 		case DSM_IMPL_SYSV:
 			return dsm_impl_sysv(op, handle, request_size, impl_private,
-								 mapped_address, mapped_size, elevel);
+								 mapped_address, mapped_size, elevel, flags);
 #endif
 #ifdef USE_DSM_WINDOWS
 		case DSM_IMPL_WINDOWS:
 			return dsm_impl_windows(op, handle, request_size, impl_private,
-									mapped_address, mapped_size, elevel);
+									mapped_address, mapped_size, elevel, flags);
 #endif
 #ifdef USE_DSM_MMAP
 		case DSM_IMPL_MMAP:
 			return dsm_impl_mmap(op, handle, request_size, impl_private,
-								 mapped_address, mapped_size, elevel);
+								 mapped_address, mapped_size, elevel, flags);
 #endif
 		default:
 			elog(ERROR, "unexpected dynamic shared memory type: %d",
@@ -211,10 +218,10 @@ dsm_impl_op(dsm_op op, dsm_handle handle, Size request_size,
 static bool
 dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
 			   void **impl_private, void **mapped_address, Size *mapped_size,
-			   int elevel)
+			   int elevel, int flags)
 {
 	char		name[64];
-	int			flags;
+	int			open_flags;
 	int			fd;
 	char	   *address;
 
@@ -255,10 +262,13 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
 	 */
 	ReserveExternalFD();
 
-	flags = O_RDWR | (op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
-	if ((fd = shm_open(name, flags, PG_FILE_MODE_OWNER)) == -1)
+	open_flags = O_RDWR | (op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
+	if ((fd = shm_open(name, open_flags, PG_FILE_MODE_OWNER)) == -1)
 	{
 		ReleaseExternalFD();
+		if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+			(errno == ENOMEM || errno == ENOSPC))
+			return false;
 		if (op == DSM_OP_ATTACH || errno != EEXIST)
 			ereport(elevel,
 					(errcode_for_dynamic_shared_memory(),
@@ -304,6 +314,9 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
 		shm_unlink(name);
 		errno = save_errno;
 
+		if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+			(errno == ENOMEM || errno == ENOSPC))
+			return false;
 		ereport(elevel,
 				(errcode_for_dynamic_shared_memory(),
 				 errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
@@ -326,6 +339,9 @@ dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
 			shm_unlink(name);
 		errno = save_errno;
 
+		if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+			(errno == ENOMEM || errno == ENOSPC))
+			return false;
 		ereport(elevel,
 				(errcode_for_dynamic_shared_memory(),
 				 errmsg("could not map shared memory segment \"%s\": %m",
@@ -422,7 +438,7 @@ dsm_impl_posix_resize(int fd, off_t size)
 static bool
 dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
 			  void **impl_private, void **mapped_address, Size *mapped_size,
-			  int elevel)
+			  int elevel, int flags)
 {
 	key_t		key;
 	int			ident;
@@ -484,13 +500,14 @@ dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
 	}
 	else
 	{
-		int			flags = IPCProtection;
+		int			ipc_flags = IPCProtection;
 		size_t		segsize;
 
 		/*
 		 * Allocate the memory BEFORE acquiring the resource, so that we don't
 		 * leak the resource if memory allocation fails.
 		 */
+		// FIXME: NO_OOM
 		ident_cache = MemoryContextAlloc(TopMemoryContext, sizeof(int));
 
 		/*
@@ -502,16 +519,20 @@ dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
 
 		if (op == DSM_OP_CREATE)
 		{
-			flags |= IPC_CREAT | IPC_EXCL;
+			ipc_flags |= IPC_CREAT | IPC_EXCL;
 			segsize = request_size;
 		}
 
-		if ((ident = shmget(key, segsize, flags)) == -1)
+		if ((ident = shmget(key, segsize, ipc_flags)) == -1)
 		{
+			if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+				(errno == ENOMEM || errno == ENOSPC))
+				return false;
 			if (op == DSM_OP_ATTACH || errno != EEXIST)
 			{
 				int			save_errno = errno;
 
+				// FIXME: do we leak 'ident_cache' otherwise?
 				pfree(ident_cache);
 				errno = save_errno;
 				ereport(elevel,
@@ -579,6 +600,9 @@ dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
 			shmctl(ident, IPC_RMID, NULL);
 		errno = save_errno;
 
+		if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+			(errno == ENOMEM || errno == ENOSPC))
+			return false;
 		ereport(elevel,
 				(errcode_for_dynamic_shared_memory(),
 				 errmsg("could not map shared memory segment \"%s\": %m",
@@ -609,7 +633,7 @@ dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
 static bool
 dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size,
 				 void **impl_private, void **mapped_address,
-				 Size *mapped_size, int elevel)
+				 Size *mapped_size, int elevel, int flags)
 {
 	char	   *address;
 	HANDLE		hmap;
@@ -702,6 +726,10 @@ dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size,
 		if (!hmap)
 		{
 			_dosmaperr(errcode);
+
+			if ((flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+				(errno == ENOMEM || errno == ENOSPC))
+				return false;
 			ereport(elevel,
 					(errcode_for_dynamic_shared_memory(),
 					 errmsg("could not create shared memory segment \"%s\": %m",
@@ -738,6 +766,9 @@ dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size,
 		CloseHandle(hmap);
 		errno = save_errno;
 
+		if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+			(errno == ENOMEM || errno == ENOSPC))
+			return false;
 		ereport(elevel,
 				(errcode_for_dynamic_shared_memory(),
 				 errmsg("could not map shared memory segment \"%s\": %m",
@@ -791,10 +822,10 @@ dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size,
 static bool
 dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
 			  void **impl_private, void **mapped_address, Size *mapped_size,
-			  int elevel)
+			  int elevel, int flags)
 {
 	char		name[64];
-	int			flags;
+	int			open_flags;
 	int			fd;
 	char	   *address;
 
@@ -827,9 +858,12 @@ dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
 	}
 
 	/* Create new segment or open an existing one for attach. */
-	flags = O_RDWR | (op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
-	if ((fd = OpenTransientFile(name, flags)) == -1)
+	open_flags = O_RDWR | (op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
+	if ((fd = OpenTransientFile(name, open_flags)) == -1)
 	{
+		if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+			(errno == ENOMEM || errno == ENOSPC))
+			return false;
 		if (op == DSM_OP_ATTACH || errno != EEXIST)
 			ereport(elevel,
 					(errcode_for_dynamic_shared_memory(),
@@ -906,6 +940,9 @@ dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
 			unlink(name);
 			errno = save_errno ? save_errno : ENOSPC;
 
+			if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+				(errno == ENOMEM || errno == ENOSPC))
+				return false;
 			ereport(elevel,
 					(errcode_for_dynamic_shared_memory(),
 					 errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
@@ -928,6 +965,9 @@ dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
 			unlink(name);
 		errno = save_errno;
 
+		if (op == DSM_OP_CREATE && (flags & DSM_OP_CREATE_NO_OOM) != 0 &&
+			(errno == ENOMEM || errno == ENOSPC))
+			return false;
 		ereport(elevel,
 				(errcode_for_dynamic_shared_memory(),
 				 errmsg("could not map shared memory segment \"%s\": %m",
diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c
index a6b728ba9ff..29b6f975be7 100644
--- a/src/backend/utils/mmgr/dsa.c
+++ b/src/backend/utils/mmgr/dsa.c
@@ -2164,7 +2164,7 @@ make_new_segment(dsa_area *area, size_t requested_pages)
 	/* Create the segment. */
 	oldowner = CurrentResourceOwner;
 	CurrentResourceOwner = area->resowner;
-	segment = dsm_create(total_size, 0);
+	segment = dsm_create(total_size, DSM_CREATE_NULL_IF_MAXSEGMENTS | DSM_CREATE_NO_OOM);
 	CurrentResourceOwner = oldowner;
 	if (segment == NULL)
 		return NULL;
diff --git a/src/include/storage/dsm.h b/src/include/storage/dsm.h
index 1a22b32df1a..7e2ec8fc560 100644
--- a/src/include/storage/dsm.h
+++ b/src/include/storage/dsm.h
@@ -18,6 +18,7 @@
 typedef struct dsm_segment dsm_segment;
 
 #define DSM_CREATE_NULL_IF_MAXSEGMENTS			0x0001
+#define DSM_CREATE_NO_OOM						0x0002
 
 /* Startup and shutdown functions. */
 struct PGShmemHeader;			/* avoid including pg_shmem.h */
diff --git a/src/include/storage/dsm_impl.h b/src/include/storage/dsm_impl.h
index 882269603da..2097876f756 100644
--- a/src/include/storage/dsm_impl.h
+++ b/src/include/storage/dsm_impl.h
@@ -66,10 +66,12 @@ typedef enum
 	DSM_OP_DESTROY,
 } dsm_op;
 
+#define DSM_OP_CREATE_NO_OOM	0x01
+
 /* Create, attach to, detach from, resize, or destroy a segment. */
 extern bool dsm_impl_op(dsm_op op, dsm_handle handle, Size request_size,
 						void **impl_private, void **mapped_address, Size *mapped_size,
-						int elevel);
+						int elevel, int flags);
 
 /* Implementation-dependent actions required to keep segment until shutdown. */
 extern void dsm_impl_pin_segment(dsm_handle handle, void *impl_private,
-- 
2.39.2

Reply via email to