1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00
Files
postgres/src/backend/storage/ipc/dsm_registry.c
Bruce Momjian 50e6eb731d Update copyright for 2025
Backpatch-through: 13
2025-01-01 11:21:55 -05:00

201 lines
5.2 KiB
C

/*-------------------------------------------------------------------------
*
* 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-2025, 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_strcmp,
dshash_strhash,
dshash_strcpy,
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;
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();
entry = dshash_find_or_insert(dsm_registry_table, name, 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
{
dsm_segment *seg = dsm_find_mapping(entry->handle);
/* If the existing segment is not already attached, attach it now. */
if (seg == NULL)
{
seg = dsm_attach(entry->handle);
if (seg == NULL)
elog(ERROR, "could not map dynamic shared memory segment");
dsm_pin_mapping(seg);
}
ret = dsm_segment_address(seg);
}
dshash_release_lock(dsm_registry_table, entry);
MemoryContextSwitchTo(oldcontext);
return ret;
}