diff options
author | Tom Lane | 2016-12-12 18:32:10 +0000 |
---|---|---|
committer | Tom Lane | 2016-12-12 18:32:10 +0000 |
commit | be7b2848c6d8bdbfb63ab403c535713708c4af52 (patch) | |
tree | 3c6df9ff960885b283593a0e1366672f13fe7737 /src/backend/port/posix_sema.c | |
parent | 06e184876bc07c2b1d7144957dcf02c5b4f709c2 (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/posix_sema.c')
-rw-r--r-- | src/backend/port/posix_sema.c | 98 |
1 files changed, 81 insertions, 17 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; } /* |