From 6b8238cb6aa76924fa63640fe421c421d2fa5472 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 26 Mar 2026 11:35:55 +0200 Subject: [PATCH] Refactor ShmemIndex initialization Initialize the ShmemIndex hash table in InitShmemAllocator() already, removing the need for the separate InitShmemIndex() step. Reviewed-by: Ashutosh Bapat Discussion: https://www.postgresql.org/message-id/CAExHW5vM1bneLYfg0wGeAa=52UiJ3z4vKd3AJ72X8Fw6k3KKrg@mail.gmail.com --- src/backend/storage/ipc/ipci.c | 8 +- src/backend/storage/ipc/shmem.c | 143 ++++++++++++-------------------- src/include/storage/shmem.h | 1 - 3 files changed, 56 insertions(+), 96 deletions(-) diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index a4785daf1e5..3d3f153809b 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -250,16 +250,10 @@ static void CreateOrAttachShmemStructs(void) { /* - * Now initialize LWLocks, which do shared memory allocation and are - * needed for InitShmemIndex. + * Now initialize LWLocks, which do shared memory allocation. */ CreateLWLocks(); - /* - * Set up shmem.c index hashtable - */ - InitShmemIndex(); - dsm_shmem_init(); DSMRegistryShmemInit(); diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index 0424c445723..0fb3bc59929 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -124,6 +124,14 @@ Datum pg_numa_available(PG_FUNCTION_ARGS); void InitShmemAllocator(PGShmemHeader *seghdr) { + Size offset; + HASHCTL info; + int hash_flags; + size_t size; + +#ifndef EXEC_BACKEND + Assert(!IsUnderPostmaster); +#endif Assert(seghdr != NULL); /* @@ -133,45 +141,58 @@ InitShmemAllocator(PGShmemHeader *seghdr) Assert(seghdr == (void *) MAXALIGN(seghdr)); Assert(seghdr->content_offset == MAXALIGN(seghdr->content_offset)); + /* + * 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. + */ + offset = CACHELINEALIGN(seghdr->content_offset + sizeof(ShmemAllocatorData)); + if (offset > seghdr->totalsize) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of shared memory (%zu bytes requested)", + offset))); + + /* + * In postmaster or stand-alone backend, initialize the shared memory + * allocator and the spinlock so that we can allocate shared memory for + * ShmemIndex using ShmemAlloc(). In a regular backend just set up the + * pointers required by ShmemAlloc(). + */ + ShmemAllocator = (ShmemAllocatorData *) ((char *) seghdr + seghdr->content_offset); + if (!IsUnderPostmaster) + { + SpinLockInit(&ShmemAllocator->shmem_lock); + ShmemAllocator->free_offset = offset; + } + + ShmemLock = &ShmemAllocator->shmem_lock; ShmemSegHdr = seghdr; ShmemBase = seghdr; ShmemEnd = (char *) ShmemBase + seghdr->totalsize; -#ifndef EXEC_BACKEND - Assert(!IsUnderPostmaster); -#endif - if (IsUnderPostmaster) + /* + * Create (or attach to) the shared memory index of shmem areas. + * + * This is the same initialization as ShmemInitHash() does, but we cannot + * use ShmemInitHash() here because it relies on ShmemIndex being already + * initialized. + */ + info.keysize = SHMEM_INDEX_KEYSIZE; + info.entrysize = sizeof(ShmemIndexEnt); + info.dsize = info.max_dsize = hash_select_dirsize(SHMEM_INDEX_SIZE); + info.alloc = ShmemAllocNoError; + hash_flags = HASH_ELEM | HASH_STRINGS | HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE; + if (!IsUnderPostmaster) { - PGShmemHeader *shmhdr = ShmemSegHdr; - - ShmemAllocator = (ShmemAllocatorData *) ((char *) shmhdr + shmhdr->content_offset); - ShmemLock = &ShmemAllocator->shmem_lock; + size = hash_get_shared_size(&info, hash_flags); + ShmemAllocator->index = (HASHHDR *) ShmemAlloc(size); } else - { - Size offset; - - /* - * 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. - */ - offset = CACHELINEALIGN(seghdr->content_offset + sizeof(ShmemAllocatorData)); - if (offset > seghdr->totalsize) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of shared memory (%zu bytes requested)", - offset))); - - ShmemAllocator = (ShmemAllocatorData *) ((char *) seghdr + seghdr->content_offset); - - SpinLockInit(&ShmemAllocator->shmem_lock); - ShmemLock = &ShmemAllocator->shmem_lock; - ShmemAllocator->free_offset = offset; - /* ShmemIndex can't be set up yet (need LWLocks first) */ - ShmemAllocator->index = NULL; - ShmemIndex = (HTAB *) NULL; - } + hash_flags |= HASH_ATTACH; + info.hctl = ShmemAllocator->index; + ShmemIndex = hash_create("ShmemIndex", SHMEM_INDEX_SIZE, &info, hash_flags); + Assert(ShmemIndex != NULL); } /* @@ -270,31 +291,6 @@ ShmemAddrIsValid(const void *addr) return (addr >= ShmemBase) && (addr < ShmemEnd); } -/* - * InitShmemIndex() --- set up or attach to shmem index table. - */ -void -InitShmemIndex(void) -{ - HASHCTL info; - - /* - * Create the shared memory shmem index. - * - * Since ShmemInitHash calls ShmemInitStruct, which expects the ShmemIndex - * hashtable to exist already, we have a bit of a circularity problem in - * initializing the ShmemIndex itself. The special "ShmemIndex" hash - * table name will tell ShmemInitStruct to fake it. - */ - info.keysize = SHMEM_INDEX_KEYSIZE; - info.entrysize = sizeof(ShmemIndexEnt); - - ShmemIndex = ShmemInitHash("ShmemIndex", - SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE, - &info, - HASH_ELEM | HASH_STRINGS); -} - /* * ShmemInitHash -- Create and initialize, or attach to, a * shared memory hash table. @@ -383,39 +379,10 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr) ShmemIndexEnt *result; void *structPtr; + Assert(ShmemIndex != NULL); + LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE); - if (!ShmemIndex) - { - /* Must be trying to create/attach to ShmemIndex itself */ - Assert(strcmp(name, "ShmemIndex") == 0); - - if (IsUnderPostmaster) - { - /* Must be initializing a (non-standalone) backend */ - Assert(ShmemAllocator->index != NULL); - structPtr = ShmemAllocator->index; - *foundPtr = true; - } - else - { - /* - * If the shmem index doesn't exist, we are bootstrapping: we must - * be trying to init the shmem index itself. - * - * Notice that the ShmemIndexLock is released before the shmem - * index has been initialized. This should be OK because no other - * process can be accessing shared memory yet. - */ - Assert(ShmemAllocator->index == NULL); - structPtr = ShmemAlloc(size); - ShmemAllocator->index = structPtr; - *foundPtr = false; - } - LWLockRelease(ShmemIndexLock); - return structPtr; - } - /* look it up in the shmem index */ result = (ShmemIndexEnt *) hash_search(ShmemIndex, name, HASH_ENTER_NULL, foundPtr); diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h index 89d45287c17..0de8a36429b 100644 --- a/src/include/storage/shmem.h +++ b/src/include/storage/shmem.h @@ -33,7 +33,6 @@ extern void InitShmemAllocator(PGShmemHeader *seghdr); extern void *ShmemAlloc(Size size); extern void *ShmemAllocNoError(Size size); extern bool ShmemAddrIsValid(const void *addr); -extern void InitShmemIndex(void); extern HTAB *ShmemInitHash(const char *name, int64 init_size, int64 max_size, HASHCTL *infoP, int hash_flags); extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);