I wrote:
> This is from current macOS, but equivalent text appears on Linux and
> in the POSIX spec.  So it's just luck that nobody has reported the
> same problem elsewhere --- unless maybe there is some macOS-specific
> behavior making it more likely that different installs would try the
> same key.

Hmm, no, there is a platform dependency here.  I made the attached
test program to see what happens when there's a key collision,
and on Linux I get

semget(SEMAS_PER_SET) failed: File exists
semget(SEMAS_PER_SET + 1) failed: File exists

but macOS and NetBSD give

semget(SEMAS_PER_SET) failed: File exists
semget(SEMAS_PER_SET + 1) failed: Invalid argument

I didn't try other BSDen; this might be a NetBSD-ism that Apple
inherited, or maybe it's common among the BSDen.  I don't see any
text in POSIX specifying which errno is to be returned in this case,
so we can't really argue that the behavior is wrong.

                        regards, tom lane

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>

typedef key_t IpcSemaphoreKey;	/* semaphore key passed to semget(2) */

#ifndef HAVE_UNION_SEMUN
union semun
{
	int			val;
	struct semid_ds *buf;
	unsigned short *array;
};
#endif

#define SEMAS_PER_SET	16

#define IPCProtection	(0600)	/* access/modify by user only */


int main(int argc, char **argv)
{
	IpcSemaphoreKey semKey = 42;
	int			semId;
	int			semId2;
	union semun semun;

	/*
	 * Find a free semaphore key, and create a set with SEMAS_PER_SET semas.
	 */
	for(;;)
	{
		semId = semget(semKey, SEMAS_PER_SET,
					   IPC_CREAT | IPC_EXCL | IPCProtection);
		if (semId >= 0)
			break;
		semKey++;
	}

	/*
	 * Check behavior if we try to make another set with same # of semas.
	 */
	semId2 = semget(semKey, SEMAS_PER_SET,
					IPC_CREAT | IPC_EXCL | IPCProtection);
	if (semId2 < 0)
		fprintf(stderr, "semget(SEMAS_PER_SET) failed: %s\n",
				strerror(errno));
	else
		fprintf(stderr, "semget(SEMAS_PER_SET) unexpectedly succeeded\n");

	/*
	 * Check behavior if we try to make another set with more semas.
	 */
	semId2 = semget(semKey, SEMAS_PER_SET + 1,
					IPC_CREAT | IPC_EXCL | IPCProtection);
	if (semId2 < 0)
		fprintf(stderr, "semget(SEMAS_PER_SET + 1) failed: %s\n",
				strerror(errno));
	else
		fprintf(stderr, "semget(SEMAS_PER_SET + 1) unexpectedly succeeded\n");

	/*
	 * Clean up
	 */
	semun.val = 0;				/* unused, but keep compiler quiet */

	if (semctl(semId, 0, IPC_RMID, semun) < 0)
		fprintf(stderr, "semctl(%d, 0, IPC_RMID, ...) failed: %s\n",
				semId, strerror(errno));

	return 0;
}

Reply via email to