mirror of https://github.com/postgres/postgres
As proof of concept, provide an alternate implementation based on POSIX semaphores. Also push the SysV shared-memory implementation into a separate file so that it can be replaced conveniently.ecpg_big_bison
parent
91fc10fdac
commit
72a3902a66
@ -0,0 +1,357 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* posix_sema.c |
||||||
|
* Implement PGSemaphores using POSIX semaphore facilities |
||||||
|
* |
||||||
|
* We prefer the unnamed style of POSIX semaphore (the kind made with |
||||||
|
* sem_init). We can cope with the kind made with sem_open, however. |
||||||
|
* |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* $Header: /cvsroot/pgsql/src/backend/port/posix_sema.c,v 1.1 2002/05/05 00:03:28 tgl Exp $ |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#include "storage/pg_sema.h" |
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_NAMED_POSIX_SEMAPHORES |
||||||
|
/* PGSemaphore is pointer to pointer to sem_t */ |
||||||
|
#define PG_SEM_REF(x) (*(x)) |
||||||
|
#else |
||||||
|
/* PGSemaphore is pointer to sem_t */ |
||||||
|
#define PG_SEM_REF(x) (x) |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#define IPCProtection (0600) /* access/modify by user only */ |
||||||
|
|
||||||
|
static sem_t **mySemPointers; /* keep track of created semaphores */ |
||||||
|
static int numSems; /* number of semas acquired so far */ |
||||||
|
static int maxSems; /* allocated size of mySemaPointers array */ |
||||||
|
static int nextSemKey; /* next name to try */ |
||||||
|
|
||||||
|
|
||||||
|
static void ReleaseSemaphores(int status, Datum arg); |
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_NAMED_POSIX_SEMAPHORES |
||||||
|
|
||||||
|
/*
|
||||||
|
* PosixSemaphoreCreate |
||||||
|
* |
||||||
|
* Attempt to create a new named semaphore. |
||||||
|
* |
||||||
|
* If we fail with a failure code other than collision-with-existing-sema, |
||||||
|
* print out an error and abort. Other types of errors suggest nonrecoverable |
||||||
|
* problems. |
||||||
|
*/ |
||||||
|
static sem_t * |
||||||
|
PosixSemaphoreCreate(void) |
||||||
|
{ |
||||||
|
int semKey; |
||||||
|
char semname[64]; |
||||||
|
sem_t *mySem; |
||||||
|
|
||||||
|
for (;;) |
||||||
|
{ |
||||||
|
semKey = nextSemKey++; |
||||||
|
|
||||||
|
snprintf(semname, sizeof(semname), "/pgsql-%d", semKey); |
||||||
|
|
||||||
|
mySem = sem_open(semname, O_CREAT | O_EXCL, |
||||||
|
(mode_t) IPCProtection, (unsigned) 1); |
||||||
|
if (mySem != SEM_FAILED) |
||||||
|
break; |
||||||
|
|
||||||
|
/* Loop if error indicates a collision */ |
||||||
|
if (errno == EEXIST || errno == EACCES || errno == EINTR) |
||||||
|
continue; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Else complain and abort |
||||||
|
*/ |
||||||
|
fprintf(stderr, "PosixSemaphoreCreate: sem_open(%s) failed: %s\n", |
||||||
|
semname, strerror(errno)); |
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlink the semaphore immediately, so it can't be accessed externally. |
||||||
|
* This also ensures that it will go away if we crash. |
||||||
|
*/ |
||||||
|
sem_unlink(semname); |
||||||
|
|
||||||
|
return mySem; |
||||||
|
} |
||||||
|
|
||||||
|
#else /* !USE_NAMED_POSIX_SEMAPHORES */ |
||||||
|
|
||||||
|
/*
|
||||||
|
* PosixSemaphoreCreate |
||||||
|
* |
||||||
|
* Attempt to create a new unnamed semaphore. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
PosixSemaphoreCreate(sem_t *sem) |
||||||
|
{ |
||||||
|
if (sem_init(sem, 1, 1) < 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "PosixSemaphoreCreate: sem_init failed: %s\n", |
||||||
|
strerror(errno)); |
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* USE_NAMED_POSIX_SEMAPHORES */ |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PosixSemaphoreKill - removes a semaphore |
||||||
|
*/ |
||||||
|
static void |
||||||
|
PosixSemaphoreKill(sem_t *sem) |
||||||
|
{ |
||||||
|
#ifdef USE_NAMED_POSIX_SEMAPHORES |
||||||
|
/* Got to use sem_close for named semaphores */ |
||||||
|
if (sem_close(sem) < 0) |
||||||
|
fprintf(stderr, "PosixSemaphoreKill: sem_close failed: %s\n", |
||||||
|
strerror(errno)); |
||||||
|
#else |
||||||
|
/* Got to use sem_destroy for unnamed semaphores */ |
||||||
|
if (sem_destroy(sem) < 0) |
||||||
|
fprintf(stderr, "PosixSemaphoreKill: sem_destroy failed: %s\n", |
||||||
|
strerror(errno)); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PGReserveSemaphores --- initialize semaphore support |
||||||
|
* |
||||||
|
* This is called during postmaster start or shared memory reinitialization. |
||||||
|
* It should do whatever is needed to be able to support up to maxSemas |
||||||
|
* subsequent PGSemaphoreCreate calls. Also, if any system resources |
||||||
|
* are acquired here or in PGSemaphoreCreate, register an on_shmem_exit |
||||||
|
* callback to release them. |
||||||
|
* |
||||||
|
* The port number is passed for possible use as a key (for Posix, we use |
||||||
|
* it to generate the starting semaphore name). In a standalone backend, |
||||||
|
* zero will be passed. |
||||||
|
* |
||||||
|
* In the Posix implementation, we acquire semaphores on-demand; the |
||||||
|
* maxSemas parameter is just used to size the array that keeps track of |
||||||
|
* acquired semas for subsequent releasing. |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGReserveSemaphores(int maxSemas, int port) |
||||||
|
{ |
||||||
|
mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *)); |
||||||
|
if (mySemPointers == NULL) |
||||||
|
elog(PANIC, "Out of memory in PGReserveSemaphores"); |
||||||
|
numSems = 0; |
||||||
|
maxSems = maxSemas; |
||||||
|
nextSemKey = port * 1000; |
||||||
|
|
||||||
|
on_shmem_exit(ReleaseSemaphores, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Release semaphores at shutdown or shmem reinitialization |
||||||
|
* |
||||||
|
* (called as an on_shmem_exit callback, hence funny argument list) |
||||||
|
*/ |
||||||
|
static void |
||||||
|
ReleaseSemaphores(int status, Datum arg) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
|
||||||
|
for (i = 0; i < numSems; i++) |
||||||
|
PosixSemaphoreKill(mySemPointers[i]); |
||||||
|
free(mySemPointers); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreCreate |
||||||
|
* |
||||||
|
* Initialize a PGSemaphore structure to represent a sema with count 1 |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreCreate(PGSemaphore sema) |
||||||
|
{ |
||||||
|
sem_t *newsem; |
||||||
|
|
||||||
|
/* Can't do this in a backend, because static state is postmaster's */ |
||||||
|
Assert(!IsUnderPostmaster); |
||||||
|
|
||||||
|
if (numSems >= maxSems) |
||||||
|
elog(PANIC, "PGSemaphoreCreate: too many semaphores created"); |
||||||
|
|
||||||
|
#ifdef USE_NAMED_POSIX_SEMAPHORES |
||||||
|
*sema = newsem = PosixSemaphoreCreate(); |
||||||
|
#else |
||||||
|
PosixSemaphoreCreate(sema); |
||||||
|
newsem = sema; |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Remember new sema for ReleaseSemaphores */ |
||||||
|
mySemPointers[numSems++] = newsem; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreReset |
||||||
|
* |
||||||
|
* Reset a previously-initialized PGSemaphore to have count 0 |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreReset(PGSemaphore sema) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* There's no direct API for this in POSIX, so we have to ratchet the |
||||||
|
* semaphore down to 0 with repeated trywait's. |
||||||
|
*/ |
||||||
|
for (;;) |
||||||
|
{ |
||||||
|
if (sem_trywait(PG_SEM_REF(sema)) < 0) |
||||||
|
{ |
||||||
|
if (errno == EAGAIN || errno == EDEADLK) |
||||||
|
break; /* got it down to 0 */ |
||||||
|
if (errno == EINTR) |
||||||
|
continue; /* can this happen? */ |
||||||
|
fprintf(stderr, "PGSemaphoreReset: sem_trywait failed: %s\n", |
||||||
|
strerror(errno)); |
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreLock |
||||||
|
* |
||||||
|
* Lock a semaphore (decrement count), blocking if count would be < 0 |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreLock(PGSemaphore sema, bool interruptOK) |
||||||
|
{ |
||||||
|
int errStatus; |
||||||
|
|
||||||
|
/*
|
||||||
|
* 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. We |
||||||
|
* assume that if such an interrupt comes in while we are waiting, it |
||||||
|
* will cause the sem_wait() call to exit with errno == EINTR, so that we |
||||||
|
* will be able to service the interrupt (if not in a critical section |
||||||
|
* already). |
||||||
|
* |
||||||
|
* 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 sem_wait() 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 sem_wait()). 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 |
||||||
|
* sem_wait() 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 Posix semaphores used to |
||||||
|
* implement LW locks or emulate spinlocks --- but the wait time for |
||||||
|
* such locks should not be very long, anyway.) |
||||||
|
*/ |
||||||
|
do |
||||||
|
{ |
||||||
|
ImmediateInterruptOK = interruptOK; |
||||||
|
CHECK_FOR_INTERRUPTS(); |
||||||
|
errStatus = sem_wait(PG_SEM_REF(sema)); |
||||||
|
ImmediateInterruptOK = false; |
||||||
|
} while (errStatus < 0 && errno == EINTR); |
||||||
|
|
||||||
|
if (errStatus < 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "PGSemaphoreLock: sem_wait failed: %s\n", |
||||||
|
strerror(errno)); |
||||||
|
proc_exit(255); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreUnlock |
||||||
|
* |
||||||
|
* Unlock a semaphore (increment count) |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreUnlock(PGSemaphore sema) |
||||||
|
{ |
||||||
|
int errStatus; |
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = sem_post(PG_SEM_REF(sema)); |
||||||
|
} while (errStatus < 0 && errno == EINTR); |
||||||
|
|
||||||
|
if (errStatus < 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "PGSemaphoreUnlock: sem_post failed: %s\n", |
||||||
|
strerror(errno)); |
||||||
|
proc_exit(255); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreTryLock |
||||||
|
* |
||||||
|
* Lock a semaphore only if able to do so without blocking |
||||||
|
*/ |
||||||
|
bool |
||||||
|
PGSemaphoreTryLock(PGSemaphore sema) |
||||||
|
{ |
||||||
|
int errStatus; |
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = sem_trywait(PG_SEM_REF(sema)); |
||||||
|
} while (errStatus < 0 && errno == EINTR); |
||||||
|
|
||||||
|
if (errStatus < 0) |
||||||
|
{ |
||||||
|
if (errno == EAGAIN || errno == EDEADLK) |
||||||
|
return false; /* failed to lock it */ |
||||||
|
/* Otherwise we got trouble */ |
||||||
|
fprintf(stderr, "PGSemaphoreTryLock: sem_trywait failed: %s\n", |
||||||
|
strerror(errno)); |
||||||
|
proc_exit(255); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
@ -0,0 +1,522 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* sysv_sema.c |
||||||
|
* Implement PGSemaphores using SysV semaphore facilities |
||||||
|
* |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* $Header: /cvsroot/pgsql/src/backend/port/sysv_sema.c,v 1.1 2002/05/05 00:03:28 tgl Exp $ |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <sys/file.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#ifdef HAVE_SYS_IPC_H |
||||||
|
#include <sys/ipc.h> |
||||||
|
#endif |
||||||
|
#ifdef HAVE_SYS_SEM_H |
||||||
|
#include <sys/sem.h> |
||||||
|
#endif |
||||||
|
#ifdef HAVE_KERNEL_OS_H |
||||||
|
#include <kernel/OS.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "miscadmin.h" |
||||||
|
#include "storage/ipc.h" |
||||||
|
#include "storage/pg_sema.h" |
||||||
|
|
||||||
|
|
||||||
|
#ifndef HAVE_UNION_SEMUN |
||||||
|
union semun |
||||||
|
{ |
||||||
|
int val; |
||||||
|
struct semid_ds *buf; |
||||||
|
unsigned short *array; |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef uint32 IpcSemaphoreKey; /* semaphore key passed to semget(2) */ |
||||||
|
typedef int IpcSemaphoreId; /* semaphore ID returned by semget(2) */ |
||||||
|
|
||||||
|
/*
|
||||||
|
* SEMAS_PER_SET is the number of useful semaphores in each semaphore set |
||||||
|
* we allocate. It must be *less than* your kernel's SEMMSL (max semaphores |
||||||
|
* per set) parameter, which is often around 25. (Less than, because we |
||||||
|
* allocate one extra sema in each set for identification purposes.) |
||||||
|
*/ |
||||||
|
#define SEMAS_PER_SET 16 |
||||||
|
|
||||||
|
#define IPCProtection (0600) /* access/modify by user only */ |
||||||
|
|
||||||
|
#define PGSemaMagic 537 /* must be less than SEMVMX */ |
||||||
|
|
||||||
|
|
||||||
|
static IpcSemaphoreId *mySemaSets; /* IDs of sema sets acquired so far */ |
||||||
|
static int numSemaSets; /* number of sema sets acquired so far */ |
||||||
|
static int maxSemaSets; /* allocated size of mySemaSets array */ |
||||||
|
static IpcSemaphoreKey nextSemaKey; /* next key to try using */ |
||||||
|
static int nextSemaNumber; /* next free sem num in last sema set */ |
||||||
|
|
||||||
|
|
||||||
|
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); |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InternalIpcSemaphoreCreate |
||||||
|
* |
||||||
|
* Attempt to create a new semaphore set with the specified key. |
||||||
|
* Will fail (return -1) if such a set already exists. |
||||||
|
* |
||||||
|
* If we fail with a failure code other than collision-with-existing-set, |
||||||
|
* print out an error and abort. Other types of errors suggest nonrecoverable |
||||||
|
* problems. |
||||||
|
*/ |
||||||
|
static IpcSemaphoreId |
||||||
|
InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems) |
||||||
|
{ |
||||||
|
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 |
||||||
|
*/ |
||||||
|
fprintf(stderr, "IpcSemaphoreCreate: semget(key=%d, num=%d, 0%o) failed: %s\n", |
||||||
|
(int) semKey, numSems, (IPC_CREAT | IPC_EXCL | IPCProtection), |
||||||
|
strerror(errno)); |
||||||
|
|
||||||
|
if (errno == ENOSPC) |
||||||
|
fprintf(stderr, |
||||||
|
"\nThis error does *not* mean that you have run out of disk space.\n" |
||||||
|
"\n" |
||||||
|
"It occurs when either the system limit for the maximum number of\n" |
||||||
|
"semaphore sets (SEMMNI), or the system wide maximum number of\n" |
||||||
|
"semaphores (SEMMNS), would be exceeded. You need to raise the\n" |
||||||
|
"respective kernel parameter. Alternatively, reduce PostgreSQL's\n" |
||||||
|
"consumption of semaphores by reducing its max_connections parameter\n" |
||||||
|
"(currently %d).\n" |
||||||
|
"\n" |
||||||
|
"The PostgreSQL Administrator's Guide contains more information about\n" |
||||||
|
"configuring your system for PostgreSQL.\n\n", |
||||||
|
MaxBackends); |
||||||
|
|
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
return semId; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize a semaphore to the specified value. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum, int value) |
||||||
|
{ |
||||||
|
union semun semun; |
||||||
|
|
||||||
|
semun.val = value; |
||||||
|
if (semctl(semId, semNum, SETVAL, semun) < 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "IpcSemaphoreInitialize: semctl(id=%d, %d, SETVAL, %d) failed: %s\n", |
||||||
|
semId, semNum, value, strerror(errno)); |
||||||
|
|
||||||
|
if (errno == ERANGE) |
||||||
|
fprintf(stderr, |
||||||
|
"You possibly need to raise your kernel's SEMVMX value to be at least\n" |
||||||
|
"%d. Look into the PostgreSQL documentation for details.\n", |
||||||
|
value); |
||||||
|
|
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* IpcSemaphoreKill(semId) - removes a semaphore set |
||||||
|
*/ |
||||||
|
static void |
||||||
|
IpcSemaphoreKill(IpcSemaphoreId semId) |
||||||
|
{ |
||||||
|
union semun semun; |
||||||
|
|
||||||
|
semun.val = 0; /* unused, but keep compiler quiet */ |
||||||
|
|
||||||
|
if (semctl(semId, 0, IPC_RMID, semun) < 0) |
||||||
|
fprintf(stderr, "IpcSemaphoreKill: semctl(%d, 0, IPC_RMID, ...) failed: %s\n", |
||||||
|
semId, strerror(errno)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* We used to report a failure via elog(WARNING), but that's pretty |
||||||
|
* pointless considering any client has long since disconnected ... |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
/* Get the current value (semval) of the semaphore */ |
||||||
|
static int |
||||||
|
IpcSemaphoreGetValue(IpcSemaphoreId semId, int semNum) |
||||||
|
{ |
||||||
|
union semun dummy; /* for Solaris */ |
||||||
|
|
||||||
|
dummy.val = 0; /* unused */ |
||||||
|
|
||||||
|
return semctl(semId, semNum, GETVAL, dummy); |
||||||
|
} |
||||||
|
|
||||||
|
/* Get the PID of the last process to do semop() on the semaphore */ |
||||||
|
static pid_t |
||||||
|
IpcSemaphoreGetLastPID(IpcSemaphoreId semId, int semNum) |
||||||
|
{ |
||||||
|
union semun dummy; /* for Solaris */ |
||||||
|
|
||||||
|
dummy.val = 0; /* unused */ |
||||||
|
|
||||||
|
return semctl(semId, semNum, GETPID, dummy); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a semaphore set with the given number of useful semaphores |
||||||
|
* (an additional sema is actually allocated to serve as identifier). |
||||||
|
* Dead Postgres sema sets are recycled if found, but we do not fail |
||||||
|
* upon collision with non-Postgres sema sets. |
||||||
|
* |
||||||
|
* The idea here is to detect and re-use keys that may have been assigned |
||||||
|
* by a crashed postmaster or backend. |
||||||
|
*/ |
||||||
|
static IpcSemaphoreId |
||||||
|
IpcSemaphoreCreate(int numSems) |
||||||
|
{ |
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PGReserveSemaphores --- initialize semaphore support |
||||||
|
* |
||||||
|
* This is called during postmaster start or shared memory reinitialization. |
||||||
|
* It should do whatever is needed to be able to support up to maxSemas |
||||||
|
* subsequent PGSemaphoreCreate calls. Also, if any system resources |
||||||
|
* are acquired here or in PGSemaphoreCreate, register an on_shmem_exit |
||||||
|
* callback to release them. |
||||||
|
* |
||||||
|
* The port number is passed for possible use as a key (for SysV, we use |
||||||
|
* it to generate the starting semaphore key). In a standalone backend, |
||||||
|
* zero will be passed. |
||||||
|
* |
||||||
|
* In the SysV implementation, we acquire semaphore sets on-demand; the |
||||||
|
* maxSemas parameter is just used to size the array that keeps track of |
||||||
|
* acquired sets for subsequent releasing. |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGReserveSemaphores(int maxSemas, int port) |
||||||
|
{ |
||||||
|
maxSemaSets = (maxSemas + SEMAS_PER_SET-1) / SEMAS_PER_SET; |
||||||
|
mySemaSets = (IpcSemaphoreId *) |
||||||
|
malloc(maxSemaSets * sizeof(IpcSemaphoreId)); |
||||||
|
if (mySemaSets == NULL) |
||||||
|
elog(PANIC, "Out of memory in PGReserveSemaphores"); |
||||||
|
numSemaSets = 0; |
||||||
|
nextSemaKey = port * 1000; |
||||||
|
nextSemaNumber = SEMAS_PER_SET; /* force sema set alloc on 1st call */ |
||||||
|
|
||||||
|
on_shmem_exit(ReleaseSemaphores, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Release semaphores at shutdown or shmem reinitialization |
||||||
|
* |
||||||
|
* (called as an on_shmem_exit callback, hence funny argument list) |
||||||
|
*/ |
||||||
|
static void |
||||||
|
ReleaseSemaphores(int status, Datum arg) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
|
||||||
|
for (i = 0; i < numSemaSets; i++) |
||||||
|
IpcSemaphoreKill(mySemaSets[i]); |
||||||
|
free(mySemaSets); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreCreate |
||||||
|
* |
||||||
|
* Initialize a PGSemaphore structure to represent a sema with count 1 |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreCreate(PGSemaphore sema) |
||||||
|
{ |
||||||
|
/* 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, "PGSemaphoreCreate: 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); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreReset |
||||||
|
* |
||||||
|
* Reset a previously-initialized PGSemaphore to have count 0 |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreReset(PGSemaphore sema) |
||||||
|
{ |
||||||
|
IpcSemaphoreInitialize(sema->semId, sema->semNum, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreLock |
||||||
|
* |
||||||
|
* Lock a semaphore (decrement count), blocking if count would be < 0 |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreLock(PGSemaphore sema, bool interruptOK) |
||||||
|
{ |
||||||
|
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. We |
||||||
|
* assume that if such an interrupt comes in while we are waiting, it |
||||||
|
* will cause the semop() call to exit with errno == EINTR, so that we |
||||||
|
* will be able to service the interrupt (if not in a critical section |
||||||
|
* already). |
||||||
|
* |
||||||
|
* 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.) |
||||||
|
*/ |
||||||
|
do |
||||||
|
{ |
||||||
|
ImmediateInterruptOK = interruptOK; |
||||||
|
CHECK_FOR_INTERRUPTS(); |
||||||
|
errStatus = semop(sema->semId, &sops, 1); |
||||||
|
ImmediateInterruptOK = false; |
||||||
|
} while (errStatus < 0 && errno == EINTR); |
||||||
|
|
||||||
|
if (errStatus < 0) |
||||||
|
{ |
||||||
|
fprintf(stderr, "PGSemaphoreLock: semop(id=%d) failed: %s\n", |
||||||
|
sema->semId, strerror(errno)); |
||||||
|
proc_exit(255); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreUnlock |
||||||
|
* |
||||||
|
* Unlock a semaphore (increment count) |
||||||
|
*/ |
||||||
|
void |
||||||
|
PGSemaphoreUnlock(PGSemaphore sema) |
||||||
|
{ |
||||||
|
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) |
||||||
|
{ |
||||||
|
fprintf(stderr, "PGSemaphoreUnlock: semop(id=%d) failed: %s\n", |
||||||
|
sema->semId, strerror(errno)); |
||||||
|
proc_exit(255); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreTryLock |
||||||
|
* |
||||||
|
* Lock a semaphore only if able to do so without blocking |
||||||
|
*/ |
||||||
|
bool |
||||||
|
PGSemaphoreTryLock(PGSemaphore sema) |
||||||
|
{ |
||||||
|
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 */ |
||||||
|
fprintf(stderr, "PGSemaphoreTryLock: semop(id=%d) failed: %s\n", |
||||||
|
sema->semId, strerror(errno)); |
||||||
|
proc_exit(255); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
@ -0,0 +1,400 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* sysv_shmem.c |
||||||
|
* Implement shared memory using SysV facilities |
||||||
|
* |
||||||
|
* These routines represent a fairly thin layer on top of SysV shared |
||||||
|
* memory functionality. |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* $Header: /cvsroot/pgsql/src/backend/port/sysv_shmem.c,v 1.1 2002/05/05 00:03:28 tgl Exp $ |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <sys/file.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#ifdef HAVE_SYS_IPC_H |
||||||
|
#include <sys/ipc.h> |
||||||
|
#endif |
||||||
|
#ifdef HAVE_SYS_SHM_H |
||||||
|
#include <sys/shm.h> |
||||||
|
#endif |
||||||
|
#ifdef HAVE_KERNEL_OS_H |
||||||
|
#include <kernel/OS.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "miscadmin.h" |
||||||
|
#include "storage/ipc.h" |
||||||
|
#include "storage/pg_shmem.h" |
||||||
|
|
||||||
|
|
||||||
|
typedef uint32 IpcMemoryKey; /* shared memory key passed to shmget(2) */ |
||||||
|
typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */ |
||||||
|
|
||||||
|
#define IPCProtection (0600) /* access/modify by user only */ |
||||||
|
|
||||||
|
|
||||||
|
static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size); |
||||||
|
static void IpcMemoryDetach(int status, Datum shmaddr); |
||||||
|
static void IpcMemoryDelete(int status, Datum shmId); |
||||||
|
static void *PrivateMemoryCreate(uint32 size); |
||||||
|
static void PrivateMemoryDelete(int status, Datum memaddr); |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InternalIpcMemoryCreate(memKey, size) |
||||||
|
* |
||||||
|
* Attempt to create a new shared memory segment with the specified key. |
||||||
|
* Will fail (return NULL) if such a segment already exists. If successful, |
||||||
|
* attach the segment to the current process and return its attached address. |
||||||
|
* On success, callbacks are registered with on_shmem_exit to detach and |
||||||
|
* delete the segment when on_shmem_exit is called. |
||||||
|
* |
||||||
|
* If we fail with a failure code other than collision-with-existing-segment, |
||||||
|
* print out an error and abort. Other types of errors are not recoverable. |
||||||
|
*/ |
||||||
|
static void * |
||||||
|
InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size) |
||||||
|
{ |
||||||
|
IpcMemoryId shmid; |
||||||
|
void *memAddress; |
||||||
|
|
||||||
|
shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection); |
||||||
|
|
||||||
|
if (shmid < 0) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* Fail quietly if error indicates a collision with existing |
||||||
|
* segment. 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 seg is slated for destruction but |
||||||
|
* not gone yet. |
||||||
|
*/ |
||||||
|
if (errno == EEXIST || errno == EACCES |
||||||
|
#ifdef EIDRM |
||||||
|
|| errno == EIDRM |
||||||
|
#endif |
||||||
|
) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Else complain and abort |
||||||
|
*/ |
||||||
|
fprintf(stderr, "IpcMemoryCreate: shmget(key=%d, size=%u, 0%o) failed: %s\n", |
||||||
|
(int) memKey, size, (IPC_CREAT | IPC_EXCL | IPCProtection), |
||||||
|
strerror(errno)); |
||||||
|
|
||||||
|
if (errno == EINVAL) |
||||||
|
fprintf(stderr, |
||||||
|
"\nThis error usually means that PostgreSQL's request for a shared memory\n" |
||||||
|
"segment exceeded your kernel's SHMMAX parameter. You can either\n" |
||||||
|
"reduce the request size or reconfigure the kernel with larger SHMMAX.\n" |
||||||
|
"To reduce the request size (currently %u bytes), reduce\n" |
||||||
|
"PostgreSQL's shared_buffers parameter (currently %d) and/or\n" |
||||||
|
"its max_connections parameter (currently %d).\n" |
||||||
|
"\n" |
||||||
|
"If the request size is already small, it's possible that it is less than\n" |
||||||
|
"your kernel's SHMMIN parameter, in which case raising the request size or\n" |
||||||
|
"reconfiguring SHMMIN is called for.\n" |
||||||
|
"\n" |
||||||
|
"The PostgreSQL Administrator's Guide contains more information about\n" |
||||||
|
"shared memory configuration.\n\n", |
||||||
|
size, NBuffers, MaxBackends); |
||||||
|
|
||||||
|
else if (errno == ENOMEM) |
||||||
|
fprintf(stderr, |
||||||
|
"\nThis error usually means that PostgreSQL's request for a shared\n" |
||||||
|
"memory segment exceeded available memory or swap space.\n" |
||||||
|
"To reduce the request size (currently %u bytes), reduce\n" |
||||||
|
"PostgreSQL's shared_buffers parameter (currently %d) and/or\n" |
||||||
|
"its max_connections parameter (currently %d).\n" |
||||||
|
"\n" |
||||||
|
"The PostgreSQL Administrator's Guide contains more information about\n" |
||||||
|
"shared memory configuration.\n\n", |
||||||
|
size, NBuffers, MaxBackends); |
||||||
|
|
||||||
|
else if (errno == ENOSPC) |
||||||
|
fprintf(stderr, |
||||||
|
"\nThis error does *not* mean that you have run out of disk space.\n" |
||||||
|
"\n" |
||||||
|
"It occurs either if all available shared memory IDs have been taken,\n" |
||||||
|
"in which case you need to raise the SHMMNI parameter in your kernel,\n" |
||||||
|
"or because the system's overall limit for shared memory has been\n" |
||||||
|
"reached. If you cannot increase the shared memory limit,\n" |
||||||
|
"reduce PostgreSQL's shared memory request (currently %u bytes),\n" |
||||||
|
"by reducing its shared_buffers parameter (currently %d) and/or\n" |
||||||
|
"its max_connections parameter (currently %d).\n" |
||||||
|
"\n" |
||||||
|
"The PostgreSQL Administrator's Guide contains more information about\n" |
||||||
|
"shared memory configuration.\n\n", |
||||||
|
size, NBuffers, MaxBackends); |
||||||
|
|
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
/* Register on-exit routine to delete the new segment */ |
||||||
|
on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid)); |
||||||
|
|
||||||
|
/* OK, should be able to attach to the segment */ |
||||||
|
#if defined(solaris) && defined(__sparc__) |
||||||
|
/* use intimate shared memory on SPARC Solaris */ |
||||||
|
memAddress = shmat(shmid, 0, SHM_SHARE_MMU); |
||||||
|
#else |
||||||
|
memAddress = shmat(shmid, 0, 0); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (memAddress == (void *) -1) |
||||||
|
{ |
||||||
|
fprintf(stderr, "IpcMemoryCreate: shmat(id=%d) failed: %s\n", |
||||||
|
shmid, strerror(errno)); |
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
/* Register on-exit routine to detach new segment before deleting */ |
||||||
|
on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress)); |
||||||
|
|
||||||
|
/* Record key and ID in lockfile for data directory. */ |
||||||
|
RecordSharedMemoryInLockFile((unsigned long) memKey, |
||||||
|
(unsigned long) shmid); |
||||||
|
|
||||||
|
return memAddress; |
||||||
|
} |
||||||
|
|
||||||
|
/****************************************************************************/ |
||||||
|
/* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */ |
||||||
|
/* from process' address spaceq */ |
||||||
|
/* (called as an on_shmem_exit callback, hence funny argument list) */ |
||||||
|
/****************************************************************************/ |
||||||
|
static void |
||||||
|
IpcMemoryDetach(int status, Datum shmaddr) |
||||||
|
{ |
||||||
|
if (shmdt(DatumGetPointer(shmaddr)) < 0) |
||||||
|
fprintf(stderr, "IpcMemoryDetach: shmdt(%p) failed: %s\n", |
||||||
|
DatumGetPointer(shmaddr), strerror(errno)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* We used to report a failure via elog(WARNING), but that's pretty |
||||||
|
* pointless considering any client has long since disconnected ... |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
/****************************************************************************/ |
||||||
|
/* IpcMemoryDelete(status, shmId) deletes a shared memory segment */ |
||||||
|
/* (called as an on_shmem_exit callback, hence funny argument list) */ |
||||||
|
/****************************************************************************/ |
||||||
|
static void |
||||||
|
IpcMemoryDelete(int status, Datum shmId) |
||||||
|
{ |
||||||
|
if (shmctl(DatumGetInt32(shmId), IPC_RMID, (struct shmid_ds *) NULL) < 0) |
||||||
|
fprintf(stderr, "IpcMemoryDelete: shmctl(%d, %d, 0) failed: %s\n", |
||||||
|
DatumGetInt32(shmId), IPC_RMID, strerror(errno)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* We used to report a failure via elog(WARNING), but that's pretty |
||||||
|
* pointless considering any client has long since disconnected ... |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSharedMemoryIsInUse |
||||||
|
* |
||||||
|
* Is a previously-existing shmem segment still existing and in use? |
||||||
|
*/ |
||||||
|
bool |
||||||
|
PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2) |
||||||
|
{ |
||||||
|
IpcMemoryId shmId = (IpcMemoryId) id2; |
||||||
|
struct shmid_ds shmStat; |
||||||
|
|
||||||
|
/*
|
||||||
|
* We detect whether a shared memory segment is in use by seeing |
||||||
|
* whether it (a) exists and (b) has any processes are attached to it. |
||||||
|
* |
||||||
|
* If we are unable to perform the stat operation for a reason other than |
||||||
|
* nonexistence of the segment (most likely, because it doesn't belong |
||||||
|
* to our userid), assume it is in use. |
||||||
|
*/ |
||||||
|
if (shmctl(shmId, IPC_STAT, &shmStat) < 0) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* EINVAL actually has multiple possible causes documented in the |
||||||
|
* shmctl man page, but we assume it must mean the segment no |
||||||
|
* longer exists. |
||||||
|
*/ |
||||||
|
if (errno == EINVAL) |
||||||
|
return false; |
||||||
|
/* Else assume segment is in use */ |
||||||
|
return true; |
||||||
|
} |
||||||
|
/* If it has attached processes, it's in use */ |
||||||
|
if (shmStat.shm_nattch != 0) |
||||||
|
return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* private memory support |
||||||
|
* |
||||||
|
* Rather than allocating shmem segments with IPC_PRIVATE key, we |
||||||
|
* just malloc() the requested amount of space. This code emulates |
||||||
|
* the needed shmem functions. |
||||||
|
* ---------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
|
||||||
|
static void * |
||||||
|
PrivateMemoryCreate(uint32 size) |
||||||
|
{ |
||||||
|
void *memAddress; |
||||||
|
|
||||||
|
memAddress = malloc(size); |
||||||
|
if (!memAddress) |
||||||
|
{ |
||||||
|
fprintf(stderr, "PrivateMemoryCreate: malloc(%u) failed\n", size); |
||||||
|
proc_exit(1); |
||||||
|
} |
||||||
|
MemSet(memAddress, 0, size); /* keep Purify quiet */ |
||||||
|
|
||||||
|
/* Register on-exit routine to release storage */ |
||||||
|
on_shmem_exit(PrivateMemoryDelete, PointerGetDatum(memAddress)); |
||||||
|
|
||||||
|
return memAddress; |
||||||
|
} |
||||||
|
|
||||||
|
static void |
||||||
|
PrivateMemoryDelete(int status, Datum memaddr) |
||||||
|
{ |
||||||
|
free(DatumGetPointer(memaddr)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSharedMemoryCreate |
||||||
|
* |
||||||
|
* Create a shared memory segment of the given size and initialize its |
||||||
|
* standard header. Also, register an on_shmem_exit callback to release |
||||||
|
* the storage. |
||||||
|
* |
||||||
|
* Dead Postgres segments are recycled if found, but we do not fail upon |
||||||
|
* collision with non-Postgres shmem segments. The idea here is to detect and |
||||||
|
* re-use keys that may have been assigned by a crashed postmaster or backend. |
||||||
|
* |
||||||
|
* The port number is passed for possible use as a key (for SysV, we use |
||||||
|
* it to generate the starting shmem key). In a standalone backend, |
||||||
|
* zero will be passed. |
||||||
|
*/ |
||||||
|
PGShmemHeader * |
||||||
|
PGSharedMemoryCreate(uint32 size, bool makePrivate, int port) |
||||||
|
{ |
||||||
|
IpcMemoryKey NextShmemSegID; |
||||||
|
void *memAddress; |
||||||
|
PGShmemHeader *hdr; |
||||||
|
|
||||||
|
/* Room for a header? */ |
||||||
|
Assert(size > MAXALIGN(sizeof(PGShmemHeader))); |
||||||
|
|
||||||
|
/* Loop till we find a free IPC key */ |
||||||
|
NextShmemSegID = port * 1000; |
||||||
|
|
||||||
|
for (NextShmemSegID++;; NextShmemSegID++) |
||||||
|
{ |
||||||
|
IpcMemoryId shmid; |
||||||
|
|
||||||
|
/* Special case if creating a private segment --- just malloc() it */ |
||||||
|
if (makePrivate) |
||||||
|
{ |
||||||
|
memAddress = PrivateMemoryCreate(size); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/* Try to create new segment */ |
||||||
|
memAddress = InternalIpcMemoryCreate(NextShmemSegID, size); |
||||||
|
if (memAddress) |
||||||
|
break; /* successful create and attach */ |
||||||
|
|
||||||
|
/* See if it looks to be leftover from a dead Postgres process */ |
||||||
|
shmid = shmget(NextShmemSegID, sizeof(PGShmemHeader), 0); |
||||||
|
if (shmid < 0) |
||||||
|
continue; /* failed: must be some other app's */ |
||||||
|
|
||||||
|
#if defined(solaris) && defined(__sparc__) |
||||||
|
/* use intimate shared memory on SPARC Solaris */ |
||||||
|
memAddress = shmat(shmid, 0, SHM_SHARE_MMU); |
||||||
|
#else |
||||||
|
memAddress = shmat(shmid, 0, 0); |
||||||
|
#endif |
||||||
|
|
||||||
|
if (memAddress == (void *) -1) |
||||||
|
continue; /* failed: must be some other app's */ |
||||||
|
hdr = (PGShmemHeader *) memAddress; |
||||||
|
if (hdr->magic != PGShmemMagic) |
||||||
|
{ |
||||||
|
shmdt(memAddress); |
||||||
|
continue; /* segment 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. |
||||||
|
*/ |
||||||
|
if (hdr->creatorPID != getpid()) |
||||||
|
{ |
||||||
|
if (kill(hdr->creatorPID, 0) == 0 || |
||||||
|
errno != ESRCH) |
||||||
|
{ |
||||||
|
shmdt(memAddress); |
||||||
|
continue; /* segment belongs to a live process */ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* The segment 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 segment belongs to someone else after all, and continue |
||||||
|
* quietly. |
||||||
|
*/ |
||||||
|
shmdt(memAddress); |
||||||
|
if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0) |
||||||
|
continue; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Now try again to create the segment. |
||||||
|
*/ |
||||||
|
memAddress = InternalIpcMemoryCreate(NextShmemSegID, size); |
||||||
|
if (memAddress) |
||||||
|
break; /* successful create and attach */ |
||||||
|
|
||||||
|
/*
|
||||||
|
* Can only get here if some other process managed to create the |
||||||
|
* same shmem key before we did. Let him have that one, loop |
||||||
|
* around to try next key. |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* OK, we created a new segment. Mark it as created by this process. |
||||||
|
* The order of assignments here is critical so that another Postgres |
||||||
|
* process can't see the header as valid but belonging to an invalid |
||||||
|
* PID! |
||||||
|
*/ |
||||||
|
hdr = (PGShmemHeader *) memAddress; |
||||||
|
hdr->creatorPID = getpid(); |
||||||
|
hdr->magic = PGShmemMagic; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize space allocation status for segment. |
||||||
|
*/ |
||||||
|
hdr->totalsize = size; |
||||||
|
hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader)); |
||||||
|
|
||||||
|
return hdr; |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* pg_sema.h |
||||||
|
* Platform-independent API for semaphores. |
||||||
|
* |
||||||
|
* PostgreSQL requires counting semaphores (the kind that keep track of |
||||||
|
* multiple unlock operations, and will allow an equal number of subsequent |
||||||
|
* lock operations before blocking). The underlying implementation is |
||||||
|
* not the same on every platform. This file defines the API that must |
||||||
|
* be provided by each port. |
||||||
|
* |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* $Id: pg_sema.h,v 1.1 2002/05/05 00:03:29 tgl Exp $ |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#ifndef PG_SEMA_H |
||||||
|
#define PG_SEMA_H |
||||||
|
|
||||||
|
/*
|
||||||
|
* PGSemaphoreData and pointer type PGSemaphore are the data structure |
||||||
|
* representing an individual semaphore. The contents of PGSemaphoreData |
||||||
|
* vary across implementations and must never be touched by platform- |
||||||
|
* independent code. PGSemaphoreData structures are always allocated |
||||||
|
* in shared memory (to support implementations where the data changes during |
||||||
|
* lock/unlock). |
||||||
|
* |
||||||
|
* pg_config.h must define exactly one of the USE_xxx_SEMAPHORES symbols. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifdef USE_NAMED_POSIX_SEMAPHORES |
||||||
|
|
||||||
|
#include <semaphore.h> |
||||||
|
|
||||||
|
typedef sem_t *PGSemaphoreData; |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef USE_UNNAMED_POSIX_SEMAPHORES |
||||||
|
|
||||||
|
#include <semaphore.h> |
||||||
|
|
||||||
|
typedef sem_t PGSemaphoreData; |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef USE_SYSV_SEMAPHORES |
||||||
|
|
||||||
|
typedef struct PGSemaphoreData |
||||||
|
{ |
||||||
|
int semId; /* semaphore set identifier */ |
||||||
|
int semNum; /* semaphore number within set */ |
||||||
|
} PGSemaphoreData; |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
typedef PGSemaphoreData *PGSemaphore; |
||||||
|
|
||||||
|
|
||||||
|
/* Module initialization (called during postmaster start or shmem reinit) */ |
||||||
|
extern void PGReserveSemaphores(int maxSemas, int port); |
||||||
|
/* Initialize a PGSemaphore structure to represent a sema with count 1 */ |
||||||
|
extern void PGSemaphoreCreate(PGSemaphore sema); |
||||||
|
/* Reset a previously-initialized PGSemaphore to have count 0 */ |
||||||
|
extern void PGSemaphoreReset(PGSemaphore sema); |
||||||
|
/* Lock a semaphore (decrement count), blocking if count would be < 0 */ |
||||||
|
extern void PGSemaphoreLock(PGSemaphore sema, bool interruptOK); |
||||||
|
/* Unlock a semaphore (increment count) */ |
||||||
|
extern void PGSemaphoreUnlock(PGSemaphore sema); |
||||||
|
/* Lock a semaphore only if able to do so without blocking */ |
||||||
|
extern bool PGSemaphoreTryLock(PGSemaphore sema); |
||||||
|
|
||||||
|
#endif /* PG_SEMA_H */ |
@ -0,0 +1,44 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* pg_shmem.h |
||||||
|
* Platform-independent API for shared memory support. |
||||||
|
* |
||||||
|
* Every port is expected to support shared memory with approximately |
||||||
|
* SysV-ish semantics; in particular, a memory block is not anonymous |
||||||
|
* but has an ID, and we must be able to tell whether there are any |
||||||
|
* remaining processes attached to a block of a specified ID. |
||||||
|
* |
||||||
|
* To simplify life for the SysV implementation, the ID is assumed to |
||||||
|
* consist of two unsigned long values (these are key and ID in SysV |
||||||
|
* terms). Other platforms may ignore the second value if they need |
||||||
|
* only one ID number. |
||||||
|
* |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* $Id: pg_shmem.h,v 1.1 2002/05/05 00:03:29 tgl Exp $ |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#ifndef PG_SHMEM_H |
||||||
|
#define PG_SHMEM_H |
||||||
|
|
||||||
|
#include <sys/types.h> |
||||||
|
|
||||||
|
|
||||||
|
typedef struct PGShmemHeader /* standard header for all Postgres shmem */ |
||||||
|
{ |
||||||
|
int32 magic; /* magic # to identify Postgres segments */ |
||||||
|
#define PGShmemMagic 679834892 |
||||||
|
pid_t creatorPID; /* PID of creating process */ |
||||||
|
uint32 totalsize; /* total size of segment */ |
||||||
|
uint32 freeoffset; /* offset to first free space */ |
||||||
|
} PGShmemHeader; |
||||||
|
|
||||||
|
|
||||||
|
extern PGShmemHeader *PGSharedMemoryCreate(uint32 size, bool makePrivate, |
||||||
|
int port); |
||||||
|
extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); |
||||||
|
|
||||||
|
#endif /* PG_SHMEM_H */ |
@ -1,3 +1,6 @@ |
|||||||
# -traditional-cpp means "don't use apple's cpp-precomp" on darwin |
# -traditional-cpp means "don't use apple's cpp-precomp" on darwin |
||||||
# this should change to -no-cpp-precomp when that flag is implemented |
# this should change to -no-cpp-precomp when that flag is implemented |
||||||
CC="$CC -traditional-cpp" |
CC="$CC -traditional-cpp" |
||||||
|
|
||||||
|
# Select appropriate semaphore support |
||||||
|
USE_NAMED_POSIX_SEMAPHORES=1 |
||||||
|
Loading…
Reference in new issue