From c91fcee144f86be2881eb48f215d26fcfa459c2d Mon Sep 17 00:00:00 2001 From: Gavin Panella Date: Sun, 10 Aug 2025 19:30:19 +0200 Subject: [PATCH v1] When getting EINVAL from semget(2), probe for EEXIST --- src/backend/port/sysv_sema.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c index 423b2b4f9d6..51308a651cf 100644 --- a/src/backend/port/sysv_sema.c +++ b/src/backend/port/sysv_sema.c @@ -113,6 +113,41 @@ InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems) ) return -1; + /* + * Typically EINVAL means that the number of semaphores requested is + * greater than the system imposed maximum per set, or that the set + * exists but that it has fewer than numSems semaphores in it. In the + * latter case, presumably the set was allocated by another program or + * perhaps by another version of PostgreSQL (SEMAS_PER_SET was raised + * in version 17, for example). + * + * On many platforms, including Linux, since the flags passed to + * semget(2) includes IPC_CREAT | IPC_EXCL, it would return <0 and set + * errno to EEXIST when numSems is greater than the number of + * semaphores in a preexisting set. + * + * On some platforms, e.g. macOS 15.6, when encountering an existing + * set, it appears that numSems is checked first, before considering + * flags. This means that when numSems is greater than the number of + * semaphores in a preexisting set, semget(2) returns <0 and errno is + * set to EINVAL. Hence, on these platforms, EINVAL can mean we've hit + * a system limit and thus we cannot continue, OR it can mean we've + * found an existing set, a benign and anticipated situation; see how + * EEXIST is handled above. + * + * Fortunately we can distinguish these by calling semget(2) again, + * asking for _zero_ semaphores. There is a potential race, e.g. the + * semaphore set could be removed, but if we take the stance that we + * will not use this set in any case, we need only check that errno is + * _not_ EINVAL this time. + */ + if (saved_errno == EINVAL) + { + semId = semget(semKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection); + if (semId >= 0 || errno != EINVAL) + return -1; + } + /* * Else complain and abort */ -- 2.50.1