mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Introduce the dynamic shared memory registry.
Presently, the most straightforward way for a shared library to use shared memory is to request it at server startup via a shmem_request_hook, which requires specifying the library in shared_preload_libraries. Alternatively, the library can create a dynamic shared memory (DSM) segment, but absent a shared location to store the segment's handle, other backends cannot use it. This commit introduces a registry for DSM segments so that these other backends can look up existing segments with a library-specified string. This allows libraries to easily use shared memory without needing to request it at server startup. The registry is accessed via the new GetNamedDSMSegment() function. This function handles allocating the segment and initializing it via a provided callback. If another backend already created and initialized the segment, it simply attaches the segment. GetNamedDSMSegment() locks the registry appropriately to ensure that only one backend initializes the segment and that all other backends just attach it. The registry itself is comprised of a dshash table that stores the DSM segment handles keyed by a library-specified string. Reviewed-by: Michael Paquier, Andrei Lepikhov, Nikita Malakhov, Robert Haas, Bharath Rupireddy, Zhang Mingli, Amul Sul Discussion: https://postgr.es/m/20231205034647.GA2705267%40nathanxps13
This commit is contained in:
		@@ -12,6 +12,7 @@ OBJS = \
 | 
			
		||||
	barrier.o \
 | 
			
		||||
	dsm.o \
 | 
			
		||||
	dsm_impl.o \
 | 
			
		||||
	dsm_registry.o \
 | 
			
		||||
	ipc.o \
 | 
			
		||||
	ipci.o \
 | 
			
		||||
	latch.o \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										198
									
								
								src/backend/storage/ipc/dsm_registry.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/backend/storage/ipc/dsm_registry.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
/*-------------------------------------------------------------------------
 | 
			
		||||
 *
 | 
			
		||||
 * dsm_registry.c
 | 
			
		||||
 *	  Functions for interfacing with the dynamic shared memory registry.
 | 
			
		||||
 *
 | 
			
		||||
 * This provides a way for libraries to use shared memory without needing
 | 
			
		||||
 * to request it at startup time via a shmem_request_hook.  The registry
 | 
			
		||||
 * stores dynamic shared memory (DSM) segment handles keyed by a
 | 
			
		||||
 * library-specified string.
 | 
			
		||||
 *
 | 
			
		||||
 * The registry is accessed by calling GetNamedDSMSegment().  If a segment
 | 
			
		||||
 * with the provided name does not yet exist, it is created and initialized
 | 
			
		||||
 * with the provided init_callback callback function.  Otherwise,
 | 
			
		||||
 * GetNamedDSMSegment() simply ensures that the segment is attached to the
 | 
			
		||||
 * current backend.  This function guarantees that only one backend
 | 
			
		||||
 * initializes the segment and that all other backends just attach it.
 | 
			
		||||
 *
 | 
			
		||||
 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
 | 
			
		||||
 * Portions Copyright (c) 1994, Regents of the University of California
 | 
			
		||||
 *
 | 
			
		||||
 * IDENTIFICATION
 | 
			
		||||
 *	  src/backend/storage/ipc/dsm_registry.c
 | 
			
		||||
 *
 | 
			
		||||
 *-------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "postgres.h"
 | 
			
		||||
 | 
			
		||||
#include "lib/dshash.h"
 | 
			
		||||
#include "storage/dsm_registry.h"
 | 
			
		||||
#include "storage/lwlock.h"
 | 
			
		||||
#include "storage/shmem.h"
 | 
			
		||||
#include "utils/memutils.h"
 | 
			
		||||
 | 
			
		||||
typedef struct DSMRegistryCtxStruct
 | 
			
		||||
{
 | 
			
		||||
	dsa_handle	dsah;
 | 
			
		||||
	dshash_table_handle dshh;
 | 
			
		||||
} DSMRegistryCtxStruct;
 | 
			
		||||
 | 
			
		||||
static DSMRegistryCtxStruct *DSMRegistryCtx;
 | 
			
		||||
 | 
			
		||||
typedef struct DSMRegistryEntry
 | 
			
		||||
{
 | 
			
		||||
	char		name[64];
 | 
			
		||||
	dsm_handle	handle;
 | 
			
		||||
	size_t		size;
 | 
			
		||||
} DSMRegistryEntry;
 | 
			
		||||
 | 
			
		||||
static const dshash_parameters dsh_params = {
 | 
			
		||||
	offsetof(DSMRegistryEntry, handle),
 | 
			
		||||
	sizeof(DSMRegistryEntry),
 | 
			
		||||
	dshash_memcmp,
 | 
			
		||||
	dshash_memhash,
 | 
			
		||||
	LWTRANCHE_DSM_REGISTRY_HASH
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static dsa_area *dsm_registry_dsa;
 | 
			
		||||
static dshash_table *dsm_registry_table;
 | 
			
		||||
 | 
			
		||||
Size
 | 
			
		||||
DSMRegistryShmemSize(void)
 | 
			
		||||
{
 | 
			
		||||
	return MAXALIGN(sizeof(DSMRegistryCtxStruct));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
DSMRegistryShmemInit(void)
 | 
			
		||||
{
 | 
			
		||||
	bool		found;
 | 
			
		||||
 | 
			
		||||
	DSMRegistryCtx = (DSMRegistryCtxStruct *)
 | 
			
		||||
		ShmemInitStruct("DSM Registry Data",
 | 
			
		||||
						DSMRegistryShmemSize(),
 | 
			
		||||
						&found);
 | 
			
		||||
 | 
			
		||||
	if (!found)
 | 
			
		||||
	{
 | 
			
		||||
		DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
 | 
			
		||||
		DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize or attach to the dynamic shared hash table that stores the DSM
 | 
			
		||||
 * registry entries, if not already done.  This must be called before accessing
 | 
			
		||||
 * the table.
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
init_dsm_registry(void)
 | 
			
		||||
{
 | 
			
		||||
	/* Quick exit if we already did this. */
 | 
			
		||||
	if (dsm_registry_table)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Otherwise, use a lock to ensure only one process creates the table. */
 | 
			
		||||
	LWLockAcquire(DSMRegistryLock, LW_EXCLUSIVE);
 | 
			
		||||
 | 
			
		||||
	if (DSMRegistryCtx->dshh == DSHASH_HANDLE_INVALID)
 | 
			
		||||
	{
 | 
			
		||||
		/* Initialize dynamic shared hash table for registry. */
 | 
			
		||||
		dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA);
 | 
			
		||||
		dsa_pin(dsm_registry_dsa);
 | 
			
		||||
		dsa_pin_mapping(dsm_registry_dsa);
 | 
			
		||||
		dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL);
 | 
			
		||||
 | 
			
		||||
		/* Store handles in shared memory for other backends to use. */
 | 
			
		||||
		DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa);
 | 
			
		||||
		DSMRegistryCtx->dshh = dshash_get_hash_table_handle(dsm_registry_table);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		/* Attach to existing dynamic shared hash table. */
 | 
			
		||||
		dsm_registry_dsa = dsa_attach(DSMRegistryCtx->dsah);
 | 
			
		||||
		dsa_pin_mapping(dsm_registry_dsa);
 | 
			
		||||
		dsm_registry_table = dshash_attach(dsm_registry_dsa, &dsh_params,
 | 
			
		||||
										   DSMRegistryCtx->dshh, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LWLockRelease(DSMRegistryLock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize or attach a named DSM segment.
 | 
			
		||||
 *
 | 
			
		||||
 * This routine returns the address of the segment.  init_callback is called to
 | 
			
		||||
 * initialize the segment when it is first created.
 | 
			
		||||
 */
 | 
			
		||||
void *
 | 
			
		||||
GetNamedDSMSegment(const char *name, size_t size,
 | 
			
		||||
				   void (*init_callback) (void *ptr), bool *found)
 | 
			
		||||
{
 | 
			
		||||
	DSMRegistryEntry *entry;
 | 
			
		||||
	MemoryContext oldcontext;
 | 
			
		||||
	char		name_padded[offsetof(DSMRegistryEntry, handle)] = {0};
 | 
			
		||||
	void	   *ret;
 | 
			
		||||
 | 
			
		||||
	Assert(found);
 | 
			
		||||
 | 
			
		||||
	if (!name || *name == '\0')
 | 
			
		||||
		ereport(ERROR,
 | 
			
		||||
				(errmsg("DSM segment name cannot be empty")));
 | 
			
		||||
 | 
			
		||||
	if (strlen(name) >= offsetof(DSMRegistryEntry, handle))
 | 
			
		||||
		ereport(ERROR,
 | 
			
		||||
				(errmsg("DSM segment name too long")));
 | 
			
		||||
 | 
			
		||||
	if (size == 0)
 | 
			
		||||
		ereport(ERROR,
 | 
			
		||||
				(errmsg("DSM segment size must be nonzero")));
 | 
			
		||||
 | 
			
		||||
	/* Be sure any local memory allocated by DSM/DSA routines is persistent. */
 | 
			
		||||
	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
 | 
			
		||||
 | 
			
		||||
	/* Connect to the registry. */
 | 
			
		||||
	init_dsm_registry();
 | 
			
		||||
 | 
			
		||||
	strcpy(name_padded, name);
 | 
			
		||||
	entry = dshash_find_or_insert(dsm_registry_table, name_padded, found);
 | 
			
		||||
	if (!(*found))
 | 
			
		||||
	{
 | 
			
		||||
		/* Initialize the segment. */
 | 
			
		||||
		dsm_segment *seg = dsm_create(size, 0);
 | 
			
		||||
 | 
			
		||||
		dsm_pin_segment(seg);
 | 
			
		||||
		dsm_pin_mapping(seg);
 | 
			
		||||
		entry->handle = dsm_segment_handle(seg);
 | 
			
		||||
		entry->size = size;
 | 
			
		||||
		ret = dsm_segment_address(seg);
 | 
			
		||||
 | 
			
		||||
		if (init_callback)
 | 
			
		||||
			(*init_callback) (ret);
 | 
			
		||||
	}
 | 
			
		||||
	else if (entry->size != size)
 | 
			
		||||
	{
 | 
			
		||||
		ereport(ERROR,
 | 
			
		||||
				(errmsg("requested DSM segment size does not match size of "
 | 
			
		||||
						"existing segment")));
 | 
			
		||||
	}
 | 
			
		||||
	else if (!dsm_find_mapping(entry->handle))
 | 
			
		||||
	{
 | 
			
		||||
		/* Attach to existing segment. */
 | 
			
		||||
		dsm_segment *seg = dsm_attach(entry->handle);
 | 
			
		||||
 | 
			
		||||
		dsm_pin_mapping(seg);
 | 
			
		||||
		ret = dsm_segment_address(seg);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		/* Return address of an already-attached segment. */
 | 
			
		||||
		ret = dsm_segment_address(dsm_find_mapping(entry->handle));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dshash_release_lock(dsm_registry_table, entry);
 | 
			
		||||
	MemoryContextSwitchTo(oldcontext);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -40,6 +40,7 @@
 | 
			
		||||
#include "replication/walsender.h"
 | 
			
		||||
#include "storage/bufmgr.h"
 | 
			
		||||
#include "storage/dsm.h"
 | 
			
		||||
#include "storage/dsm_registry.h"
 | 
			
		||||
#include "storage/ipc.h"
 | 
			
		||||
#include "storage/pg_shmem.h"
 | 
			
		||||
#include "storage/pmsignal.h"
 | 
			
		||||
@@ -115,6 +116,7 @@ CalculateShmemSize(int *num_semaphores)
 | 
			
		||||
	size = add_size(size, hash_estimate_size(SHMEM_INDEX_SIZE,
 | 
			
		||||
											 sizeof(ShmemIndexEnt)));
 | 
			
		||||
	size = add_size(size, dsm_estimate_size());
 | 
			
		||||
	size = add_size(size, DSMRegistryShmemSize());
 | 
			
		||||
	size = add_size(size, BufferShmemSize());
 | 
			
		||||
	size = add_size(size, LockShmemSize());
 | 
			
		||||
	size = add_size(size, PredicateLockShmemSize());
 | 
			
		||||
@@ -289,6 +291,7 @@ CreateOrAttachShmemStructs(void)
 | 
			
		||||
	InitShmemIndex();
 | 
			
		||||
 | 
			
		||||
	dsm_shmem_init();
 | 
			
		||||
	DSMRegistryShmemInit();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set up xlog, clog, and buffers
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ backend_sources += files(
 | 
			
		||||
  'barrier.c',
 | 
			
		||||
  'dsm.c',
 | 
			
		||||
  'dsm_impl.c',
 | 
			
		||||
  'dsm_registry.c',
 | 
			
		||||
  'ipc.c',
 | 
			
		||||
  'ipci.c',
 | 
			
		||||
  'latch.c',
 | 
			
		||||
 
 | 
			
		||||
@@ -190,6 +190,10 @@ static const char *const BuiltinTrancheNames[] = {
 | 
			
		||||
	"LogicalRepLauncherDSA",
 | 
			
		||||
	/* LWTRANCHE_LAUNCHER_HASH: */
 | 
			
		||||
	"LogicalRepLauncherHash",
 | 
			
		||||
	/* LWTRANCHE_DSM_REGISTRY_DSA: */
 | 
			
		||||
	"DSMRegistryDSA",
 | 
			
		||||
	/* LWTRANCHE_DSM_REGISTRY_HASH: */
 | 
			
		||||
	"DSMRegistryHash",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
 | 
			
		||||
 
 | 
			
		||||
@@ -55,3 +55,4 @@ WrapLimitsVacuumLock				46
 | 
			
		||||
NotifyQueueTailLock					47
 | 
			
		||||
WaitEventExtensionLock				48
 | 
			
		||||
WALSummarizerLock					49
 | 
			
		||||
DSMRegistryLock						50
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user