(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