#include "postgres.h"#include <signal.h>#include <unistd.h>#include <sys/file.h>#include "miscadmin.h"#include "storage/ipc.h"#include "storage/pg_sema.h"
Go to the source code of this file.
| #define IPCProtection (0600) |
Definition at line 52 of file sysv_sema.c.
Referenced by InternalIpcSemaphoreCreate().
| #define PGSemaMagic 537 |
Definition at line 54 of file sysv_sema.c.
Referenced by IpcSemaphoreCreate().
| #define SEMAS_PER_SET 16 |
Definition at line 50 of file sysv_sema.c.
Referenced by PGReserveSemaphores(), and PGSemaphoreCreate().
| typedef int IpcSemaphoreId |
Definition at line 42 of file sysv_sema.c.
| typedef key_t IpcSemaphoreKey |
Definition at line 41 of file sysv_sema.c.
| static IpcSemaphoreId InternalIpcSemaphoreCreate | ( | IpcSemaphoreKey | semKey, | |
| int | numSems | |||
| ) | [static] |
Definition at line 86 of file sysv_sema.c.
References EIDRM, ereport, errdetail(), errhint(), errmsg(), FATAL, IPC_CREAT, IPC_EXCL, and IPCProtection.
Referenced by IpcSemaphoreCreate().
{
int semId;
semId = semget(semKey, numSems, IPC_CREAT | IPC_EXCL | IPCProtection);
if (semId < 0)
{
/*
* Fail quietly if error indicates a collision with existing set. One
* would expect EEXIST, given that we said IPC_EXCL, but perhaps we
* could get a permission violation instead? Also, EIDRM might occur
* if an old set is slated for destruction but not gone yet.
*/
if (errno == EEXIST || errno == EACCES
#ifdef EIDRM
|| errno == EIDRM
#endif
)
return -1;
/*
* Else complain and abort
*/
ereport(FATAL,
(errmsg("could not create semaphores: %m"),
errdetail("Failed system call was semget(%lu, %d, 0%o).",
(unsigned long) semKey, numSems,
IPC_CREAT | IPC_EXCL | IPCProtection),
(errno == ENOSPC) ?
errhint("This error does *not* mean that you have run out of disk space. "
"It occurs when either the system limit for the maximum number of "
"semaphore sets (SEMMNI), or the system wide maximum number of "
"semaphores (SEMMNS), would be exceeded. You need to raise the "
"respective kernel parameter. Alternatively, reduce PostgreSQL's "
"consumption of semaphores by reducing its max_connections parameter.\n"
"The PostgreSQL documentation contains more information about "
"configuring your system for PostgreSQL.") : 0));
}
return semId;
}
| static IpcSemaphoreId IpcSemaphoreCreate | ( | int | numSems | ) | [static] |
Definition at line 195 of file sysv_sema.c.
References InternalIpcSemaphoreCreate(), IPC_RMID, IpcSemaphoreGetLastPID(), IpcSemaphoreGetValue(), IpcSemaphoreInitialize(), nextSemaKey, PGSemaMagic, PGSemaphoreUnlock(), and semun::val.
Referenced by PGSemaphoreCreate().
{
IpcSemaphoreId semId;
union semun semun;
PGSemaphoreData mysema;
/* Loop till we find a free IPC key */
for (nextSemaKey++;; nextSemaKey++)
{
pid_t creatorPID;
/* Try to create new semaphore set */
semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
if (semId >= 0)
break; /* successful create */
/* See if it looks to be leftover from a dead Postgres process */
semId = semget(nextSemaKey, numSems + 1, 0);
if (semId < 0)
continue; /* failed: must be some other app's */
if (IpcSemaphoreGetValue(semId, numSems) != PGSemaMagic)
continue; /* sema belongs to a non-Postgres app */
/*
* If the creator PID is my own PID or does not belong to any extant
* process, it's safe to zap it.
*/
creatorPID = IpcSemaphoreGetLastPID(semId, numSems);
if (creatorPID <= 0)
continue; /* oops, GETPID failed */
if (creatorPID != getpid())
{
if (kill(creatorPID, 0) == 0 || errno != ESRCH)
continue; /* sema belongs to a live process */
}
/*
* The sema set appears to be from a dead Postgres process, or from a
* previous cycle of life in this same process. Zap it, if possible.
* This probably shouldn't fail, but if it does, assume the sema set
* belongs to someone else after all, and continue quietly.
*/
semun.val = 0; /* unused, but keep compiler quiet */
if (semctl(semId, 0, IPC_RMID, semun) < 0)
continue;
/*
* Now try again to create the sema set.
*/
semId = InternalIpcSemaphoreCreate(nextSemaKey, numSems + 1);
if (semId >= 0)
break; /* successful create */
/*
* Can only get here if some other process managed to create the same
* sema key before we did. Let him have that one, loop around to try
* next key.
*/
}
/*
* OK, we created a new sema set. Mark it as created by this process. We
* do this by setting the spare semaphore to PGSemaMagic-1 and then
* incrementing it with semop(). That leaves it with value PGSemaMagic
* and sempid referencing this process.
*/
IpcSemaphoreInitialize(semId, numSems, PGSemaMagic - 1);
mysema.semId = semId;
mysema.semNum = numSems;
PGSemaphoreUnlock(&mysema);
return semId;
}
| static pid_t IpcSemaphoreGetLastPID | ( | IpcSemaphoreId | semId, | |
| int | semNum | |||
| ) | [static] |
Definition at line 175 of file sysv_sema.c.
References GETPID, and semun::val.
Referenced by IpcSemaphoreCreate().
| static int IpcSemaphoreGetValue | ( | IpcSemaphoreId | semId, | |
| int | semNum | |||
| ) | [static] |
Definition at line 164 of file sysv_sema.c.
References GETVAL, and semun::val.
Referenced by IpcSemaphoreCreate().
| static void IpcSemaphoreInitialize | ( | IpcSemaphoreId | semId, | |
| int | semNum, | |||
| int | value | |||
| ) | [static] |
Definition at line 133 of file sysv_sema.c.
References ereport, errhint(), errmsg_internal(), FATAL, SETVAL, and semun::val.
Referenced by IpcSemaphoreCreate(), PGSemaphoreCreate(), and PGSemaphoreReset().
{
union semun semun;
semun.val = value;
if (semctl(semId, semNum, SETVAL, semun) < 0)
ereport(FATAL,
(errmsg_internal("semctl(%d, %d, SETVAL, %d) failed: %m",
semId, semNum, value),
(errno == ERANGE) ?
errhint("You possibly need to raise your kernel's SEMVMX value to be at least "
"%d. Look into the PostgreSQL documentation for details.",
value) : 0));
}
| static void IpcSemaphoreKill | ( | IpcSemaphoreId | semId | ) | [static] |
Definition at line 152 of file sysv_sema.c.
References elog, IPC_RMID, LOG, and semun::val.
Referenced by ReleaseSemaphores().
| void PGReserveSemaphores | ( | int | maxSemas, | |
| int | port | |||
| ) |
Definition at line 288 of file sysv_sema.c.
References elog, malloc, maxSemaSets, mySemaSets, nextSemaKey, nextSemaNumber, NULL, numSemaSets, on_shmem_exit(), PANIC, ReleaseSemaphores(), and SEMAS_PER_SET.
{
maxSemaSets = (maxSemas + SEMAS_PER_SET - 1) / SEMAS_PER_SET;
mySemaSets = (IpcSemaphoreId *)
malloc(maxSemaSets * sizeof(IpcSemaphoreId));
if (mySemaSets == NULL)
elog(PANIC, "out of memory");
numSemaSets = 0;
nextSemaKey = port * 1000;
nextSemaNumber = SEMAS_PER_SET; /* force sema set alloc on 1st call */
on_shmem_exit(ReleaseSemaphores, 0);
}
| void PGSemaphoreCreate | ( | PGSemaphore | sema | ) |
Definition at line 323 of file sysv_sema.c.
References Assert, elog, IpcSemaphoreCreate(), IpcSemaphoreInitialize(), IsUnderPostmaster, maxSemaSets, mySemaSets, nextSemaNumber, numSemaSets, PANIC, and SEMAS_PER_SET.
{
/* Can't do this in a backend, because static state is postmaster's */
Assert(!IsUnderPostmaster);
if (nextSemaNumber >= SEMAS_PER_SET)
{
/* Time to allocate another semaphore set */
if (numSemaSets >= maxSemaSets)
elog(PANIC, "too many semaphores created");
mySemaSets[numSemaSets] = IpcSemaphoreCreate(SEMAS_PER_SET);
numSemaSets++;
nextSemaNumber = 0;
}
/* Assign the next free semaphore in the current set */
sema->semId = mySemaSets[numSemaSets - 1];
sema->semNum = nextSemaNumber++;
/* Initialize it to count 1 */
IpcSemaphoreInitialize(sema->semId, sema->semNum, 1);
}
| void PGSemaphoreLock | ( | PGSemaphore | sema, | |
| bool | interruptOK | |||
| ) |
Definition at line 361 of file sysv_sema.c.
References CHECK_FOR_INTERRUPTS, EINTR, elog, FATAL, and ImmediateInterruptOK.
{
int errStatus;
struct sembuf sops;
sops.sem_op = -1; /* decrement */
sops.sem_flg = 0;
sops.sem_num = sema->semNum;
/*
* Note: if errStatus is -1 and errno == EINTR then it means we returned
* from the operation prematurely because we were sent a signal. So we
* try and lock the semaphore again.
*
* Each time around the loop, we check for a cancel/die interrupt. On
* some platforms, if such an interrupt comes in while we are waiting, it
* will cause the semop() call to exit with errno == EINTR, allowing us to
* service the interrupt (if not in a critical section already) during the
* next loop iteration.
*
* Once we acquire the lock, we do NOT check for an interrupt before
* returning. The caller needs to be able to record ownership of the lock
* before any interrupt can be accepted.
*
* There is a window of a few instructions between CHECK_FOR_INTERRUPTS
* and entering the semop() call. If a cancel/die interrupt occurs in
* that window, we would fail to notice it until after we acquire the lock
* (or get another interrupt to escape the semop()). We can avoid this
* problem by temporarily setting ImmediateInterruptOK to true before we
* do CHECK_FOR_INTERRUPTS; then, a die() interrupt in this interval will
* execute directly. However, there is a huge pitfall: there is another
* window of a few instructions after the semop() before we are able to
* reset ImmediateInterruptOK. If an interrupt occurs then, we'll lose
* control, which means that the lock has been acquired but our caller did
* not get a chance to record the fact. Therefore, we only set
* ImmediateInterruptOK if the caller tells us it's OK to do so, ie, the
* caller does not need to record acquiring the lock. (This is currently
* true for lockmanager locks, since the process that granted us the lock
* did all the necessary state updates. It's not true for SysV semaphores
* used to implement LW locks or emulate spinlocks --- but the wait time
* for such locks should not be very long, anyway.)
*
* On some platforms, signals marked SA_RESTART (which is most, for us)
* will not interrupt the semop(); it will just keep waiting. Therefore
* it's necessary for cancel/die interrupts to be serviced directly by the
* signal handler. On these platforms the behavior is really the same
* whether the signal arrives just before the semop() begins, or while it
* is waiting. The loop on EINTR is thus important only for other types
* of interrupts.
*/
do
{
ImmediateInterruptOK = interruptOK;
CHECK_FOR_INTERRUPTS();
errStatus = semop(sema->semId, &sops, 1);
ImmediateInterruptOK = false;
} while (errStatus < 0 && errno == EINTR);
if (errStatus < 0)
elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
}
| void PGSemaphoreReset | ( | PGSemaphore | sema | ) |
Definition at line 350 of file sysv_sema.c.
References IpcSemaphoreInitialize().
{
IpcSemaphoreInitialize(sema->semId, sema->semNum, 0);
}
| bool PGSemaphoreTryLock | ( | PGSemaphore | sema | ) |
Definition at line 459 of file sysv_sema.c.
References EAGAIN, EINTR, elog, EWOULDBLOCK, and FATAL.
{
int errStatus;
struct sembuf sops;
sops.sem_op = -1; /* decrement */
sops.sem_flg = IPC_NOWAIT; /* but don't block */
sops.sem_num = sema->semNum;
/*
* Note: if errStatus is -1 and errno == EINTR then it means we returned
* from the operation prematurely because we were sent a signal. So we
* try and lock the semaphore again.
*/
do
{
errStatus = semop(sema->semId, &sops, 1);
} while (errStatus < 0 && errno == EINTR);
if (errStatus < 0)
{
/* Expect EAGAIN or EWOULDBLOCK (platform-dependent) */
#ifdef EAGAIN
if (errno == EAGAIN)
return false; /* failed to lock it */
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
if (errno == EWOULDBLOCK)
return false; /* failed to lock it */
#endif
/* Otherwise we got trouble */
elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
}
return true;
}
| void PGSemaphoreUnlock | ( | PGSemaphore | sema | ) |
Definition at line 429 of file sysv_sema.c.
References EINTR, elog, and FATAL.
{
int errStatus;
struct sembuf sops;
sops.sem_op = 1; /* increment */
sops.sem_flg = 0;
sops.sem_num = sema->semNum;
/*
* Note: if errStatus is -1 and errno == EINTR then it means we returned
* from the operation prematurely because we were sent a signal. So we
* try and unlock the semaphore again. Not clear this can really happen,
* but might as well cope.
*/
do
{
errStatus = semop(sema->semId, &sops, 1);
} while (errStatus < 0 && errno == EINTR);
if (errStatus < 0)
elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
}
| static void ReleaseSemaphores | ( | int | status, | |
| Datum | arg | |||
| ) | [static] |
Definition at line 308 of file sysv_sema.c.
References free, i, IpcSemaphoreKill(), mySemaSets, and numSemaSets.
Referenced by PGReserveSemaphores().
{
int i;
for (i = 0; i < numSemaSets; i++)
IpcSemaphoreKill(mySemaSets[i]);
free(mySemaSets);
}
int maxSemaSets [static] |
Definition at line 59 of file sysv_sema.c.
Referenced by PGReserveSemaphores(), and PGSemaphoreCreate().
IpcSemaphoreId* mySemaSets [static] |
Definition at line 57 of file sysv_sema.c.
Referenced by PGReserveSemaphores(), PGSemaphoreCreate(), and ReleaseSemaphores().
IpcSemaphoreKey nextSemaKey [static] |
Definition at line 60 of file sysv_sema.c.
Referenced by IpcSemaphoreCreate(), and PGReserveSemaphores().
int nextSemaNumber [static] |
Definition at line 61 of file sysv_sema.c.
Referenced by PGReserveSemaphores(), and PGSemaphoreCreate().
int numSemaSets [static] |
Definition at line 58 of file sysv_sema.c.
Referenced by PGReserveSemaphores(), PGSemaphoreCreate(), and ReleaseSemaphores().
1.7.1