Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions | Variables

sysv_sema.c File Reference

#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"
Include dependency graph for sysv_sema.c:

Go to the source code of this file.

Data Structures

union  semun

Defines

#define SEMAS_PER_SET   16
#define IPCProtection   (0600)
#define PGSemaMagic   537

Typedefs

typedef key_t IpcSemaphoreKey
typedef int IpcSemaphoreId

Functions

static IpcSemaphoreId InternalIpcSemaphoreCreate (IpcSemaphoreKey semKey, int numSems)
static void IpcSemaphoreInitialize (IpcSemaphoreId semId, int semNum, int value)
static void IpcSemaphoreKill (IpcSemaphoreId semId)
static int IpcSemaphoreGetValue (IpcSemaphoreId semId, int semNum)
static pid_t IpcSemaphoreGetLastPID (IpcSemaphoreId semId, int semNum)
static IpcSemaphoreId IpcSemaphoreCreate (int numSems)
static void ReleaseSemaphores (int status, Datum arg)
void PGReserveSemaphores (int maxSemas, int port)
void PGSemaphoreCreate (PGSemaphore sema)
void PGSemaphoreReset (PGSemaphore sema)
void PGSemaphoreLock (PGSemaphore sema, bool interruptOK)
void PGSemaphoreUnlock (PGSemaphore sema)
bool PGSemaphoreTryLock (PGSemaphore sema)

Variables

static IpcSemaphoreIdmySemaSets
static int numSemaSets
static int maxSemaSets
static IpcSemaphoreKey nextSemaKey
static int nextSemaNumber

Define Documentation

#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 Documentation

typedef int IpcSemaphoreId

Definition at line 42 of file sysv_sema.c.

Definition at line 41 of file sysv_sema.c.


Function Documentation

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().

{
    union semun dummy;          /* for Solaris */

    dummy.val = 0;              /* unused */

    return semctl(semId, semNum, GETPID, dummy);
}

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().

{
    union semun dummy;          /* for Solaris */

    dummy.val = 0;              /* unused */

    return semctl(semId, semNum, GETVAL, dummy);
}

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().

{
    union semun semun;

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

    if (semctl(semId, 0, IPC_RMID, semun) < 0)
        elog(LOG, "semctl(%d, 0, IPC_RMID, ...) failed: %m", semId);
}

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);
}


Variable Documentation

int maxSemaSets [static]

Definition at line 59 of file sysv_sema.c.

Referenced by PGReserveSemaphores(), and PGSemaphoreCreate().

Definition at line 57 of file sysv_sema.c.

Referenced by PGReserveSemaphores(), PGSemaphoreCreate(), and ReleaseSemaphores().

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().