diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index fbf5a58148..04d8e6f09f 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -10,7 +10,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.47.2.1 2007/07/02 20:12:00 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.47.2.2 2010/05/01 22:46:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,6 +92,48 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size) ) return NULL; + /* + * Some BSD-derived kernels are known to return EINVAL, not EEXIST, + * if there is an existing segment but it's smaller than "size" + * (this is a result of poorly-thought-out ordering of error tests). + * To distinguish between collision and invalid size in such cases, + * we make a second try with size = 0. These kernels do not test + * size against SHMMIN in the preexisting-segment case, so we will + * not get EINVAL a second time if there is such a segment. + */ + if (errno == EINVAL) + { + int save_errno = errno; + + shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection); + + if (shmid < 0) + { + /* As above, fail quietly if we verify a collision */ + if (errno == EEXIST || errno == EACCES +#ifdef EIDRM + || errno == EIDRM +#endif + ) + return NULL; + /* Otherwise, fall through to report the original error */ + } + else + { + /* + * On most platforms we cannot get here because SHMMIN is + * greater than zero. However, if we do succeed in creating + * a zero-size segment, free it and then fall through to + * report the original error. + */ + if (shmctl(shmid, IPC_RMID, NULL) < 0) + elog(LOG, "shmctl(%d, %d, 0) failed: %m", + (int) shmid, IPC_RMID); + } + + errno = save_errno; + } + /* * Else complain and abort */