diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 377fa591a8b3abb2152f07352bbadc5317ac0e8f..a7e40cc4a5989953ecb6398f1265498ddf99e0eb 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -471,6 +471,9 @@ typedef struct slock_t *ShmemLock; VariableCache ShmemVariableCache; Backend *ShmemBackendArray; +#ifndef HAVE_SPINLOCKS + PGSemaphore SpinlockSemaArray; +#endif LWLock *LWLockArray; slock_t *ProcStructLock; PROC_HDR *ProcGlobal; @@ -5626,6 +5629,9 @@ save_backend_variables(BackendParameters *param, Port *port, param->ShmemVariableCache = ShmemVariableCache; param->ShmemBackendArray = ShmemBackendArray; +#ifndef HAVE_SPINLOCKS + param->SpinlockSemaArray = SpinlockSemaArray; +#endif param->LWLockArray = LWLockArray; param->ProcStructLock = ProcStructLock; param->ProcGlobal = ProcGlobal; @@ -5854,6 +5860,9 @@ restore_backend_variables(BackendParameters *param, Port *port) ShmemVariableCache = param->ShmemVariableCache; ShmemBackendArray = param->ShmemBackendArray; +#ifndef HAVE_SPINLOCKS + SpinlockSemaArray = param->SpinlockSemaArray; +#endif LWLockArray = param->LWLockArray; ProcStructLock = param->ProcStructLock; ProcGlobal = param->ProcGlobal; diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index 1c8783f2c085e8151f5d46798d59c4ff871f0fa7..3c04fc31baed54f45ddbe852f901554e1b154ee3 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -105,6 +105,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) * need to be so careful during the actual allocation phase. */ size = 100000; + size = add_size(size, SpinlockSemaSize()); size = add_size(size, hash_estimate_size(SHMEM_INDEX_SIZE, sizeof(ShmemIndexEnt))); size = add_size(size, BufferShmemSize()); diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index e93b988ad743d49cbc284617e346bb2820468851..70b02ca8384b87354aebe96f59a36ebdd8d073e5 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -116,9 +116,24 @@ InitShmemAllocation(void) Assert(shmhdr != NULL); /* - * Initialize the spinlock used by ShmemAlloc. We have to do the space - * allocation the hard way, since obviously ShmemAlloc can't be called - * yet. + * 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. + */ +#ifndef HAVE_SPINLOCKS + { + PGSemaphore spinsemas; + + spinsemas = (PGSemaphore) (((char *) shmhdr) + shmhdr->freeoffset); + shmhdr->freeoffset += MAXALIGN(SpinlockSemaSize()); + SpinlockSemaInit(spinsemas); + Assert(shmhdr->freeoffset <= shmhdr->totalsize); + } +#endif + + /* + * Initialize the spinlock used by ShmemAlloc; we have to do this the hard + * way, too, for the same reasons as above. */ ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset); shmhdr->freeoffset += MAXALIGN(sizeof(slock_t)); diff --git a/src/backend/storage/lmgr/spin.c b/src/backend/storage/lmgr/spin.c index 08782178de9d08d805505852bce1341a85be6c13..3d116bc7a2e4467184939216ed0100e1e8f7d5d4 100644 --- a/src/backend/storage/lmgr/spin.c +++ b/src/backend/storage/lmgr/spin.c @@ -29,6 +29,18 @@ #include "storage/spin.h" +PGSemaphore SpinlockSemaArray; + +/* + * Report the amount of shared memory needed to store semaphores for spinlock + * support. + */ +Size +SpinlockSemaSize(void) +{ + return SpinlockSemas() * sizeof(PGSemaphoreData); +} + #ifdef HAVE_SPINLOCKS /* @@ -52,22 +64,20 @@ SpinlockSemas(void) int SpinlockSemas(void) { - int nsemas; - - /* - * It would be cleaner to distribute this logic into the affected modules, - * similar to the way shmem space estimation is handled. - * - * For now, though, there are few enough users of spinlocks that we just - * keep the knowledge here. - */ - nsemas = NumLWLocks(); /* one for each lwlock */ - nsemas += NBuffers; /* one for each buffer header */ - nsemas += max_wal_senders; /* one for each wal sender process */ - nsemas += num_xloginsert_slots; /* one for each WAL insertion slot */ - nsemas += 30; /* plus a bunch for other small-scale use */ - - return nsemas; + return NUM_SPINLOCK_SEMAPHORES; +} + +/* + * Initialize semaphores. + */ +extern void +SpinlockSemaInit(PGSemaphore spinsemas) +{ + int i; + + for (i = 0; i < NUM_SPINLOCK_SEMAPHORES; ++i) + PGSemaphoreCreate(&spinsemas[i]); + SpinlockSemaArray = spinsemas; } /* @@ -77,13 +87,15 @@ SpinlockSemas(void) void s_init_lock_sema(volatile slock_t *lock) { - PGSemaphoreCreate((PGSemaphore) lock); + static int counter = 0; + + *lock = (++counter) % NUM_SPINLOCK_SEMAPHORES; } void s_unlock_sema(volatile slock_t *lock) { - PGSemaphoreUnlock((PGSemaphore) lock); + PGSemaphoreUnlock(&SpinlockSemaArray[*lock]); } bool @@ -98,7 +110,7 @@ int tas_sema(volatile slock_t *lock) { /* Note that TAS macros return 0 if *success* */ - return !PGSemaphoreTryLock((PGSemaphore) lock); + return !PGSemaphoreTryLock(&SpinlockSemaArray[*lock]); } #endif /* !HAVE_SPINLOCKS */ diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index 2387a434aa45ead4b1c8b14f86c94cf53d804b72..20c5ff0e90298f04e4ef4dbacf159d7593cfb9c0 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -56,6 +56,14 @@ */ #define NUM_USER_DEFINED_LWLOCKS 4 +/* + * When we don't have native spinlocks, we use semaphores to simulate them. + * Decreasing this value reduces consumption of OS resources; increasing it + * may improve performance, but supplying a real spinlock implementation is + * probably far better. + */ +#define NUM_SPINLOCK_SEMAPHORES 1024 + /* * Define this if you want to allow the lo_import and lo_export SQL * functions to be executed by ordinary users. By default these diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h index b52f0e7f8523ba5586e6cdb0af38dfa11707fe80..2297f772805d8d17e19e3735005b6d27cf4626fd 100644 --- a/src/include/storage/s_lock.h +++ b/src/include/storage/s_lock.h @@ -915,7 +915,7 @@ spin_delay(void) * to fall foul of kernel limits on number of semaphores, so don't use this * unless you must! The subroutines appear in spin.c. */ -typedef PGSemaphoreData slock_t; +typedef int slock_t; extern bool s_lock_free_sema(volatile slock_t *lock); extern void s_unlock_sema(volatile slock_t *lock); diff --git a/src/include/storage/spin.h b/src/include/storage/spin.h index e7201652499735595bcdd5021cb6c1e633fa71e3..2ac510db7a036931d6b24f723467fd936aaa3aa3 100644 --- a/src/include/storage/spin.h +++ b/src/include/storage/spin.h @@ -69,5 +69,11 @@ extern int SpinlockSemas(void); +extern Size SpinlockSemaSize(void); + +#ifndef HAVE_SPINLOCKS +extern void SpinlockSemaInit(PGSemaphore); +extern PGSemaphore SpinlockSemaArray; +#endif #endif /* SPIN_H */