diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/port/posix_sema.c | 98 | ||||
-rw-r--r-- | src/backend/port/sysv_sema.c | 51 | ||||
-rw-r--r-- | src/backend/port/win32_sema.c | 38 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 2 | ||||
-rw-r--r-- | src/backend/storage/ipc/ipci.c | 15 | ||||
-rw-r--r-- | src/backend/storage/ipc/procarray.c | 6 | ||||
-rw-r--r-- | src/backend/storage/ipc/shmem.c | 67 | ||||
-rw-r--r-- | src/backend/storage/lmgr/lwlock.c | 20 | ||||
-rw-r--r-- | src/backend/storage/lmgr/proc.c | 6 | ||||
-rw-r--r-- | src/backend/storage/lmgr/spin.c | 27 | ||||
-rw-r--r-- | src/include/storage/pg_sema.h | 52 | ||||
-rw-r--r-- | src/include/storage/proc.h | 4 | ||||
-rw-r--r-- | src/include/storage/shmem.h | 1 | ||||
-rw-r--r-- | src/include/storage/spin.h | 4 |
14 files changed, 268 insertions, 123 deletions
diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c index 2b4b11ce4e5..603dc5a41c2 100644 --- a/src/backend/port/posix_sema.c +++ b/src/backend/port/posix_sema.c @@ -6,6 +6,14 @@ * 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. * + * In either implementation, typedef PGSemaphore is equivalent to "sem_t *". + * With unnamed semaphores, the sem_t structs live in an array in shared + * memory. With named semaphores, that's not true because we cannot persuade + * sem_open to do its allocation there. Therefore, the named-semaphore code + * *does not cope with EXEC_BACKEND*. The sem_t structs will just be in the + * postmaster's private memory, where they are successfully inherited by + * forked backends, but they could not be accessed by exec'd backends. + * * * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -18,28 +26,38 @@ #include "postgres.h" #include <fcntl.h> +#include <semaphore.h> #include <signal.h> #include <unistd.h> #include "miscadmin.h" #include "storage/ipc.h" #include "storage/pg_sema.h" +#include "storage/shmem.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) +/* see file header comment */ +#if defined(USE_NAMED_POSIX_SEMAPHORES) && defined(EXEC_BACKEND) +#error cannot use named POSIX semaphores with EXEC_BACKEND #endif +/* typedef PGSemaphore is equivalent to pointer to sem_t */ +typedef struct PGSemaphoreData +{ + sem_t pgsem; +} PGSemaphoreData; + +#define PG_SEM_REF(x) (&(x)->pgsem) #define IPCProtection (0600) /* access/modify by user only */ +#ifdef USE_NAMED_POSIX_SEMAPHORES static sem_t **mySemPointers; /* keep track of created semaphores */ +#else +static PGSemaphore sharedSemas; /* array of PGSemaphoreData in shared memory */ +#endif static int numSems; /* number of semas acquired so far */ -static int maxSems; /* allocated size of mySemaPointers array */ +static int maxSems; /* allocated size of above arrays */ static int nextSemKey; /* next name to try */ @@ -134,6 +152,21 @@ PosixSemaphoreKill(sem_t * sem) /* + * Report amount of shared memory needed for semaphores + */ +Size +PGSemaphoreShmemSize(int maxSemas) +{ +#ifdef USE_NAMED_POSIX_SEMAPHORES + /* No shared memory needed in this case */ + return 0; +#else + /* Need a PGSemaphoreData per semaphore */ + return mul_size(maxSemas, sizeof(PGSemaphoreData)); +#endif +} + +/* * PGReserveSemaphores --- initialize semaphore support * * This is called during postmaster start or shared memory reinitialization. @@ -147,15 +180,33 @@ PosixSemaphoreKill(sem_t * sem) * 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. + * maxSemas parameter is just used to size the arrays. For unnamed + * semaphores, there is an array of PGSemaphoreData structs in shared memory. + * For named semaphores, we keep a postmaster-local array of sem_t pointers, + * which we use for releasing the semphores when done. + * (This design minimizes the dependency of postmaster shutdown on the + * contents of shared memory, which a failed backend might have clobbered. + * We can't do much about the possibility of sem_destroy() crashing, but + * we don't have to expose the counters to other processes.) */ void PGReserveSemaphores(int maxSemas, int port) { +#ifdef USE_NAMED_POSIX_SEMAPHORES mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *)); if (mySemPointers == NULL) elog(PANIC, "out of memory"); +#else + + /* + * We must use ShmemAllocUnlocked(), since the spinlock protecting + * ShmemAlloc() won't be ready yet. (This ordering is necessary when we + * are emulating spinlocks with semaphores.) + */ + sharedSemas = (PGSemaphore) + ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas)); +#endif + numSems = 0; maxSems = maxSemas; nextSemKey = port * 1000; @@ -173,19 +224,27 @@ ReleaseSemaphores(int status, Datum arg) { int i; +#ifdef USE_NAMED_POSIX_SEMAPHORES for (i = 0; i < numSems; i++) PosixSemaphoreKill(mySemPointers[i]); free(mySemPointers); +#endif + +#ifdef USE_UNNAMED_POSIX_SEMAPHORES + for (i = 0; i < numSems; i++) + PosixSemaphoreKill(PG_SEM_REF(sharedSemas + i)); +#endif } /* * PGSemaphoreCreate * - * Initialize a PGSemaphore structure to represent a sema with count 1 + * Allocate a PGSemaphore structure with initial count 1 */ -void -PGSemaphoreCreate(PGSemaphore sema) +PGSemaphore +PGSemaphoreCreate(void) { + PGSemaphore sema; sem_t *newsem; /* Can't do this in a backend, because static state is postmaster's */ @@ -195,14 +254,19 @@ PGSemaphoreCreate(PGSemaphore sema) elog(PANIC, "too many semaphores created"); #ifdef USE_NAMED_POSIX_SEMAPHORES - *sema = newsem = PosixSemaphoreCreate(); + newsem = PosixSemaphoreCreate(); + /* Remember new sema for ReleaseSemaphores */ + mySemPointers[numSems] = newsem; + sema = (PGSemaphore) newsem; #else - PosixSemaphoreCreate(sema); - newsem = sema; + sema = &sharedSemas[numSems]; + newsem = PG_SEM_REF(sema); + PosixSemaphoreCreate(newsem); #endif - /* Remember new sema for ReleaseSemaphores */ - mySemPointers[numSems++] = newsem; + numSems++; + + return sema; } /* diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c index f6f15169200..531d4265504 100644 --- a/src/backend/port/sysv_sema.c +++ b/src/backend/port/sysv_sema.c @@ -27,8 +27,15 @@ #include "miscadmin.h" #include "storage/ipc.h" #include "storage/pg_sema.h" +#include "storage/shmem.h" +typedef struct PGSemaphoreData +{ + int semId; /* semaphore set identifier */ + int semNum; /* semaphore number within set */ +} PGSemaphoreData; + #ifndef HAVE_UNION_SEMUN union semun { @@ -54,6 +61,9 @@ typedef int IpcSemaphoreId; /* semaphore ID returned by semget(2) */ #define PGSemaMagic 537 /* must be less than SEMVMX */ +static PGSemaphore sharedSemas; /* array of PGSemaphoreData in shared memory */ +static int numSharedSemas; /* number of PGSemaphoreDatas used so far */ +static int maxSharedSemas; /* allocated size of PGSemaphoreData array */ 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 */ @@ -274,6 +284,15 @@ IpcSemaphoreCreate(int numSems) /* + * Report amount of shared memory needed for semaphores + */ +Size +PGSemaphoreShmemSize(int maxSemas) +{ + return mul_size(maxSemas, sizeof(PGSemaphoreData)); +} + +/* * PGReserveSemaphores --- initialize semaphore support * * This is called during postmaster start or shared memory reinitialization. @@ -287,12 +306,26 @@ IpcSemaphoreCreate(int numSems) * 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. + * maxSemas parameter is just used to size the arrays. There is an array + * of PGSemaphoreData structs in shared memory, and a postmaster-local array + * with one entry per SysV semaphore set, which we use for releasing the + * semaphore sets when done. (This design ensures that postmaster shutdown + * doesn't rely on the contents of shared memory, which a failed backend might + * have clobbered.) */ void PGReserveSemaphores(int maxSemas, int port) { + /* + * We must use ShmemAllocUnlocked(), since the spinlock protecting + * ShmemAlloc() won't be ready yet. (This ordering is necessary when we + * are emulating spinlocks with semaphores.) + */ + sharedSemas = (PGSemaphore) + ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas)); + numSharedSemas = 0; + maxSharedSemas = maxSemas; + maxSemaSets = (maxSemas + SEMAS_PER_SET - 1) / SEMAS_PER_SET; mySemaSets = (IpcSemaphoreId *) malloc(maxSemaSets * sizeof(IpcSemaphoreId)); @@ -323,11 +356,13 @@ ReleaseSemaphores(int status, Datum arg) /* * PGSemaphoreCreate * - * Initialize a PGSemaphore structure to represent a sema with count 1 + * Allocate a PGSemaphore structure with initial count 1 */ -void -PGSemaphoreCreate(PGSemaphore sema) +PGSemaphore +PGSemaphoreCreate(void) { + PGSemaphore sema; + /* Can't do this in a backend, because static state is postmaster's */ Assert(!IsUnderPostmaster); @@ -340,11 +375,17 @@ PGSemaphoreCreate(PGSemaphore sema) numSemaSets++; nextSemaNumber = 0; } + /* Use the next shared PGSemaphoreData */ + if (numSharedSemas >= maxSharedSemas) + elog(PANIC, "too many semaphores created"); + sema = &sharedSemas[numSharedSemas++]; /* 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); + + return sema; } /* diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c index c688210e241..c8b12bedbee 100644 --- a/src/backend/port/win32_sema.c +++ b/src/backend/port/win32_sema.c @@ -23,6 +23,17 @@ static int maxSems; /* allocated size of mySemaSet array */ static void ReleaseSemaphores(int code, Datum arg); + +/* + * Report amount of shared memory needed for semaphores + */ +Size +PGSemaphoreShmemSize(int maxSemas) +{ + /* No shared memory needed on Windows */ + return 0; +} + /* * PGReserveSemaphores --- initialize semaphore support * @@ -62,10 +73,10 @@ ReleaseSemaphores(int code, Datum arg) /* * PGSemaphoreCreate * - * Initialize a PGSemaphore structure to represent a sema with count 1 + * Allocate a PGSemaphore structure with initial count 1 */ -void -PGSemaphoreCreate(PGSemaphore sema) +PGSemaphore +PGSemaphoreCreate(void) { HANDLE cur_handle; SECURITY_ATTRIBUTES sec_attrs; @@ -86,12 +97,14 @@ PGSemaphoreCreate(PGSemaphore sema) if (cur_handle) { /* Successfully done */ - *sema = cur_handle; mySemSet[numSems++] = cur_handle; } else ereport(PANIC, - (errmsg("could not create semaphore: error code %lu", GetLastError()))); + (errmsg("could not create semaphore: error code %lu", + GetLastError()))); + + return (PGSemaphore) cur_handle; } /* @@ -106,7 +119,8 @@ PGSemaphoreReset(PGSemaphore sema) * There's no direct API for this in Win32, so we have to ratchet the * semaphore down to 0 with repeated trylock's. */ - while (PGSemaphoreTryLock(sema)); + while (PGSemaphoreTryLock(sema)) + /* loop */ ; } /* @@ -127,7 +141,7 @@ PGSemaphoreLock(PGSemaphore sema) * pending signals are serviced. */ wh[0] = pgwin32_signal_event; - wh[1] = *sema; + wh[1] = sema; /* * As in other implementations of PGSemaphoreLock, we need to check for @@ -182,9 +196,10 @@ PGSemaphoreLock(PGSemaphore sema) void PGSemaphoreUnlock(PGSemaphore sema) { - if (!ReleaseSemaphore(*sema, 1, NULL)) + if (!ReleaseSemaphore(sema, 1, NULL)) ereport(FATAL, - (errmsg("could not unlock semaphore: error code %lu", GetLastError()))); + (errmsg("could not unlock semaphore: error code %lu", + GetLastError()))); } /* @@ -197,7 +212,7 @@ PGSemaphoreTryLock(PGSemaphore sema) { DWORD ret; - ret = WaitForSingleObject(*sema, 0); + ret = WaitForSingleObject(sema, 0); if (ret == WAIT_OBJECT_0) { @@ -213,7 +228,8 @@ PGSemaphoreTryLock(PGSemaphore sema) /* Otherwise we are in trouble */ ereport(FATAL, - (errmsg("could not try-lock semaphore: error code %lu", GetLastError()))); + (errmsg("could not try-lock semaphore: error code %lu", + GetLastError()))); /* keep compiler quiet */ return false; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 16dc075a3a1..535f6c4e5a0 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -484,7 +484,7 @@ typedef struct VariableCache ShmemVariableCache; Backend *ShmemBackendArray; #ifndef HAVE_SPINLOCKS - PGSemaphore SpinlockSemaArray; + PGSemaphore *SpinlockSemaArray; #endif int NamedLWLockTrancheRequests; NamedLWLockTranche *NamedLWLockTrancheArray; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 01bddcea16c..29febb46c47 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -102,6 +102,10 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) Size size; int numSemas; + /* Compute number of semaphores we'll need */ + numSemas = ProcGlobalSemas(); + numSemas += SpinlockSemas(); + /* * Size of the Postgres shared-memory block is estimated via * moderately-accurate estimates for the big hogs, plus 100K for the @@ -112,6 +116,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) * need to be so careful during the actual allocation phase. */ size = 100000; + size = add_size(size, PGSemaphoreShmemSize(numSemas)); size = add_size(size, SpinlockSemaSize()); size = add_size(size, hash_estimate_size(SHMEM_INDEX_SIZE, sizeof(ShmemIndexEnt))); @@ -166,9 +171,15 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) /* * Create semaphores */ - numSemas = ProcGlobalSemas(); - numSemas += SpinlockSemas(); PGReserveSemaphores(numSemas, port); + + /* + * If spinlocks are disabled, initialize emulation layer (which + * depends on semaphores, so the order is important here). + */ +#ifndef HAVE_SPINLOCKS + SpinlockSemaInit(); +#endif } else { diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index e5d487dbb74..bf38470f01d 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -522,7 +522,7 @@ ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid) for (;;) { /* acts as a read barrier */ - PGSemaphoreLock(&proc->sem); + PGSemaphoreLock(proc->sem); if (!proc->procArrayGroupMember) break; extraWaits++; @@ -532,7 +532,7 @@ ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid) /* Fix semaphore count for any absorbed wakeups */ while (extraWaits-- > 0) - PGSemaphoreUnlock(&proc->sem); + PGSemaphoreUnlock(proc->sem); return; } @@ -591,7 +591,7 @@ ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid) proc->procArrayGroupMember = false; if (proc != MyProc) - PGSemaphoreUnlock(&proc->sem); + PGSemaphoreUnlock(proc->sem); } } diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index cc3af2d6156..a51619407f4 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -117,36 +117,22 @@ InitShmemAllocation(void) Assert(shmhdr != NULL); /* - * If spinlocks are disabled, initialize emulation layer. We have to do - * the space allocation the hard way, since obviously ShmemAlloc can't be - * called yet. + * Initialize the spinlock used by ShmemAlloc. We must use + * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet. */ -#ifndef HAVE_SPINLOCKS - { - PGSemaphore spinsemas; + ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t)); - spinsemas = (PGSemaphore) (((char *) shmhdr) + shmhdr->freeoffset); - shmhdr->freeoffset += MAXALIGN(SpinlockSemaSize()); - SpinlockSemaInit(spinsemas); - Assert(shmhdr->freeoffset <= shmhdr->totalsize); - } -#endif + SpinLockInit(ShmemLock); /* - * Initialize the spinlock used by ShmemAlloc; we have to do this the hard - * way, too, for the same reasons as above. + * Allocations after this point should go through ShmemAlloc, which + * expects to allocate everything on cache line boundaries. Make sure the + * first allocation begins on a cache line boundary. */ - ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset); - shmhdr->freeoffset += MAXALIGN(sizeof(slock_t)); - Assert(shmhdr->freeoffset <= shmhdr->totalsize); - - /* Make sure the first allocation begins on a cache line boundary. */ aligned = (char *) (CACHELINEALIGN((((char *) shmhdr) + shmhdr->freeoffset))); shmhdr->freeoffset = aligned - (char *) shmhdr; - SpinLockInit(ShmemLock); - /* ShmemIndex can't be set up yet (need LWLocks first) */ shmhdr->index = NULL; ShmemIndex = (HTAB *) NULL; @@ -230,6 +216,45 @@ ShmemAllocNoError(Size size) } /* + * ShmemAllocUnlocked -- allocate max-aligned chunk from shared memory + * + * Allocate space without locking ShmemLock. This should be used for, + * and only for, allocations that must happen before ShmemLock is ready. + * + * We consider maxalign, rather than cachealign, sufficient here. + */ +void * +ShmemAllocUnlocked(Size size) +{ + Size newStart; + Size newFree; + void *newSpace; + + /* + * Ensure allocated space is adequately aligned. + */ + size = MAXALIGN(size); + + Assert(ShmemSegHdr != NULL); + + newStart = ShmemSegHdr->freeoffset; + + newFree = newStart + size; + if (newFree > ShmemSegHdr->totalsize) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of shared memory (%zu bytes requested)", + size))); + ShmemSegHdr->freeoffset = newFree; + + newSpace = (void *) ((char *) ShmemBase + newStart); + + Assert(newSpace == (void *) MAXALIGN(newSpace)); + + return newSpace; +} + +/* * ShmemAddrIsValid -- test if an address refers to shared memory * * Returns TRUE if the pointer points within the shared memory segment. diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index ffb2f72953c..03c4c7825eb 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -1012,7 +1012,7 @@ LWLockWakeup(LWLock *lock) */ pg_write_barrier(); waiter->lwWaiting = false; - PGSemaphoreUnlock(&waiter->sem); + PGSemaphoreUnlock(waiter->sem); } } @@ -1129,7 +1129,7 @@ LWLockDequeueSelf(LWLock *lock) */ for (;;) { - PGSemaphoreLock(&MyProc->sem); + PGSemaphoreLock(MyProc->sem); if (!MyProc->lwWaiting) break; extraWaits++; @@ -1139,7 +1139,7 @@ LWLockDequeueSelf(LWLock *lock) * Fix the process wait semaphore's count for any absorbed wakeups. */ while (extraWaits-- > 0) - PGSemaphoreUnlock(&MyProc->sem); + PGSemaphoreUnlock(MyProc->sem); } #ifdef LOCK_DEBUG @@ -1283,7 +1283,7 @@ LWLockAcquire(LWLock *lock, LWLockMode mode) for (;;) { - PGSemaphoreLock(&proc->sem); + PGSemaphoreLock(proc->sem); if (!proc->lwWaiting) break; extraWaits++; @@ -1320,7 +1320,7 @@ LWLockAcquire(LWLock *lock, LWLockMode mode) * Fix the process wait semaphore's count for any absorbed wakeups. */ while (extraWaits-- > 0) - PGSemaphoreUnlock(&proc->sem); + PGSemaphoreUnlock(proc->sem); return result; } @@ -1444,7 +1444,7 @@ LWLockAcquireOrWait(LWLock *lock, LWLockMode mode) for (;;) { - PGSemaphoreLock(&proc->sem); + PGSemaphoreLock(proc->sem); if (!proc->lwWaiting) break; extraWaits++; @@ -1481,7 +1481,7 @@ LWLockAcquireOrWait(LWLock *lock, LWLockMode mode) * Fix the process wait semaphore's count for any absorbed wakeups. */ while (extraWaits-- > 0) - PGSemaphoreUnlock(&proc->sem); + PGSemaphoreUnlock(proc->sem); if (mustwait) { @@ -1662,7 +1662,7 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval) for (;;) { - PGSemaphoreLock(&proc->sem); + PGSemaphoreLock(proc->sem); if (!proc->lwWaiting) break; extraWaits++; @@ -1692,7 +1692,7 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval) * Fix the process wait semaphore's count for any absorbed wakeups. */ while (extraWaits-- > 0) - PGSemaphoreUnlock(&proc->sem); + PGSemaphoreUnlock(proc->sem); /* * Now okay to allow cancel/die interrupts. @@ -1759,7 +1759,7 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val) /* check comment in LWLockWakeup() about this barrier */ pg_write_barrier(); waiter->lwWaiting = false; - PGSemaphoreUnlock(&waiter->sem); + PGSemaphoreUnlock(waiter->sem); } } diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 276261bd7b3..e9555f2d8fc 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -224,7 +224,7 @@ InitProcGlobal(void) */ if (i < MaxBackends + NUM_AUXILIARY_PROCS) { - PGSemaphoreCreate(&(procs[i].sem)); + procs[i].sem = PGSemaphoreCreate(); InitSharedLatch(&(procs[i].procLatch)); LWLockInitialize(&(procs[i].backendLock), LWTRANCHE_PROC); } @@ -420,7 +420,7 @@ InitProcess(void) * be careful and reinitialize its value here. (This is not strictly * necessary anymore, but seems like a good idea for cleanliness.) */ - PGSemaphoreReset(&MyProc->sem); + PGSemaphoreReset(MyProc->sem); /* * Arrange to clean up at backend exit. @@ -575,7 +575,7 @@ InitAuxiliaryProcess(void) * be careful and reinitialize its value here. (This is not strictly * necessary anymore, but seems like a good idea for cleanliness.) */ - PGSemaphoreReset(&MyProc->sem); + PGSemaphoreReset(MyProc->sem); /* * Arrange to clean up at process exit. diff --git a/src/backend/storage/lmgr/spin.c b/src/backend/storage/lmgr/spin.c index 50391414305..a6510a07e94 100644 --- a/src/backend/storage/lmgr/spin.c +++ b/src/backend/storage/lmgr/spin.c @@ -23,11 +23,12 @@ #include "postgres.h" #include "storage/pg_sema.h" +#include "storage/shmem.h" #include "storage/spin.h" #ifndef HAVE_SPINLOCKS -PGSemaphore SpinlockSemaArray; +PGSemaphore *SpinlockSemaArray; #endif /* @@ -37,7 +38,7 @@ PGSemaphore SpinlockSemaArray; Size SpinlockSemaSize(void) { - return SpinlockSemas() * sizeof(PGSemaphoreData); + return SpinlockSemas() * sizeof(PGSemaphore); } #ifdef HAVE_SPINLOCKS @@ -67,16 +68,24 @@ SpinlockSemas(void) } /* - * Initialize semaphores. + * Initialize spinlock emulation. + * + * This must be called after PGReserveSemaphores(). */ -extern void -SpinlockSemaInit(PGSemaphore spinsemas) +void +SpinlockSemaInit(void) { - int i; + PGSemaphore *spinsemas; int nsemas = SpinlockSemas(); + int i; + /* + * We must use ShmemAllocUnlocked(), since the spinlock protecting + * ShmemAlloc() obviously can't be ready yet. + */ + spinsemas = (PGSemaphore *) ShmemAllocUnlocked(SpinlockSemaSize()); for (i = 0; i < nsemas; ++i) - PGSemaphoreCreate(&spinsemas[i]); + spinsemas[i] = PGSemaphoreCreate(); SpinlockSemaArray = spinsemas; } @@ -109,7 +118,7 @@ s_unlock_sema(volatile slock_t *lock) if (lockndx <= 0 || lockndx > NUM_SPINLOCK_SEMAPHORES) elog(ERROR, "invalid spinlock number: %d", lockndx); - PGSemaphoreUnlock(&SpinlockSemaArray[lockndx - 1]); + PGSemaphoreUnlock(SpinlockSemaArray[lockndx - 1]); } bool @@ -128,7 +137,7 @@ tas_sema(volatile slock_t *lock) if (lockndx <= 0 || lockndx > NUM_SPINLOCK_SEMAPHORES) elog(ERROR, "invalid spinlock number: %d", lockndx); /* Note that TAS macros return 0 if *success* */ - return !PGSemaphoreTryLock(&SpinlockSemaArray[lockndx - 1]); + return !PGSemaphoreTryLock(SpinlockSemaArray[lockndx - 1]); } #endif /* !HAVE_SPINLOCKS */ diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h index 2c9418320e6..63546ebc96a 100644 --- a/src/include/storage/pg_sema.h +++ b/src/include/storage/pg_sema.h @@ -21,52 +21,30 @@ #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). + * struct 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; hence, PGSemaphoreData is declared as an opaque struct here. * - * pg_config.h must define exactly one of the USE_xxx_SEMAPHORES symbols. + * However, Windows is sufficiently unlike our other ports that it doesn't + * seem worth insisting on ABI compatibility for Windows too. Hence, on + * that platform just define PGSemaphore as HANDLE. */ - -#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 - -#ifdef USE_WIN32_SEMAPHORES - -typedef HANDLE PGSemaphoreData; +#ifndef USE_WIN32_SEMAPHORES +typedef struct PGSemaphoreData *PGSemaphore; +#else +typedef HANDLE PGSemaphore; #endif -typedef PGSemaphoreData *PGSemaphore; +/* Report amount of shared memory needed */ +extern Size PGSemaphoreShmemSize(int maxSemas); /* 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); +/* Allocate a PGSemaphore structure with initial count 1 */ +extern PGSemaphore PGSemaphoreCreate(void); /* Reset a previously-initialized PGSemaphore to have count 0 */ extern void PGSemaphoreReset(PGSemaphore sema); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 6fa71253d86..0344f4277c3 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -87,7 +87,7 @@ struct PGPROC SHM_QUEUE links; /* list link if process is in a list */ PGPROC **procgloballist; /* procglobal list that owns this PGPROC */ - PGSemaphoreData sem; /* ONE semaphore to sleep on */ + PGSemaphore sem; /* ONE semaphore to sleep on */ int waitStatus; /* STATUS_WAITING, STATUS_OK or STATUS_ERROR */ Latch procLatch; /* generic latch for process */ @@ -116,7 +116,7 @@ struct PGPROC proclist_node lwWaitLink; /* position in LW lock wait list */ /* Support for condition variables. */ - proclist_node cvWaitLink; /* position in CV wait list */ + proclist_node cvWaitLink; /* position in CV wait list */ /* Info about lock the process is currently waiting for, if any. */ /* waitLock and waitProcLock are NULL if not currently waiting. */ diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h index 2560e6c6da5..e4faebf2b63 100644 --- a/src/include/storage/shmem.h +++ b/src/include/storage/shmem.h @@ -36,6 +36,7 @@ extern void InitShmemAccess(void *seghdr); extern void InitShmemAllocation(void); extern void *ShmemAlloc(Size size); extern void *ShmemAllocNoError(Size size); +extern void *ShmemAllocUnlocked(Size size); extern bool ShmemAddrIsValid(const void *addr); extern void InitShmemIndex(void); extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size, diff --git a/src/include/storage/spin.h b/src/include/storage/spin.h index 50412258b9e..b95c9bc3334 100644 --- a/src/include/storage/spin.h +++ b/src/include/storage/spin.h @@ -70,8 +70,8 @@ extern int SpinlockSemas(void); extern Size SpinlockSemaSize(void); #ifndef HAVE_SPINLOCKS -extern void SpinlockSemaInit(PGSemaphore); -extern PGSemaphore SpinlockSemaArray; +extern void SpinlockSemaInit(void); +extern PGSemaphore *SpinlockSemaArray; #endif #endif /* SPIN_H */ |