summaryrefslogtreecommitdiff
path: root/src/backend/port/sysv_sema.c
diff options
context:
space:
mode:
authorTom Lane2016-12-12 18:32:10 +0000
committerTom Lane2016-12-12 18:32:10 +0000
commitbe7b2848c6d8bdbfb63ab403c535713708c4af52 (patch)
tree3c6df9ff960885b283593a0e1366672f13fe7737 /src/backend/port/sysv_sema.c
parent06e184876bc07c2b1d7144957dcf02c5b4f709c2 (diff)
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/[email protected]
Diffstat (limited to 'src/backend/port/sysv_sema.c')
-rw-r--r--src/backend/port/sysv_sema.c51
1 files changed, 46 insertions, 5 deletions
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;
}
/*