1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00

Make the different Unix-y semaphore implementations ABI-compatible.

Previously, the "sem" field of PGPROC varied in size depending on which
kernel semaphore API we were using.  That was okay as long as there was
only one likely choice per platform, but in the wake of commit ecb0d20a9,
that assumption seems rather shaky.  It doesn't seem out of the question
anymore that an extension compiled against one API choice might be loaded
into a postmaster built with another choice.  Moreover, this prevents any
possibility of selecting the semaphore API at postmaster startup, which
might be something we want to do in future.

Hence, change PGPROC.sem to be PGSemaphore (i.e. a pointer) for all Unix
semaphore APIs, and turn the pointed-to data into an opaque struct whose
contents are only known within the responsible modules.

For the SysV and unnamed-POSIX APIs, the pointed-to data has to be
allocated elsewhere in shared memory, which takes a little bit of
rejiggering of the InitShmemAllocation code sequence.  (I invented a
ShmemAllocUnlocked() function to make that a little cleaner than it used
to be.  That function is not meant for any uses other than the ones it
has now, but it beats having InitShmemAllocation() know explicitly about
allocation of space for semaphores and spinlocks.)  This change means an
extra indirection to access the semaphore data, but since we only touch
that when blocking or awakening a process, there shouldn't be any
meaningful performance penalty.  Moreover, at least for the unnamed-POSIX
case on Linux, the sem_t type is quite a bit wider than a pointer, so this
reduces sizeof(PGPROC) which seems like a good thing.

For the named-POSIX API, there's effectively no change: the PGPROC.sem
field was and still is a pointer to something returned by sem_open() in
the postmaster's memory space.  Document and check the pre-existing
limitation that this case can't work in EXEC_BACKEND mode.

It did not seem worth unifying the Windows semaphore ABI with the Unix
cases, since there's no likelihood of needing ABI compatibility much less
runtime switching across those cases.  However, we can simplify the Windows
code a bit if we define PGSemaphore as being directly a HANDLE, rather than
pointer to HANDLE, so let's do that while we're here.  (This also ends up
being no change in what's physically stored in PGPROC.sem.  We're just
moving the HANDLE fetch from callees to callers.)

It would take a bunch of additional code shuffling to get to the point of
actually choosing a semaphore API at postmaster start, but the effects
of that would now be localized in the port/XXX_sema.c files, so it seems
like fit material for a separate patch.  The need for it is unproven as
yet, anyhow, whereas the ABI risk to extensions seems real enough.

Discussion: https://postgr.es/m/4029.1481413370@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2016-12-12 13:32:10 -05:00
parent 06e184876b
commit be7b2848c6
14 changed files with 268 additions and 123 deletions

View File

@@ -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
{

View File

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

View File

@@ -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;
@@ -229,6 +215,45 @@ ShmemAllocNoError(Size size)
return newSpace;
}
/*
* 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
*