nbtree: fix read page recheck typo.
[pgsql.git] / src / backend / storage / ipc / dsm_registry.c
blob9f58ea611b913cc7c0b163e99d12499aabadf2ca
1 /*-------------------------------------------------------------------------
3 * dsm_registry.c
4 * Functions for interfacing with the dynamic shared memory registry.
6 * This provides a way for libraries to use shared memory without needing
7 * to request it at startup time via a shmem_request_hook. The registry
8 * stores dynamic shared memory (DSM) segment handles keyed by a
9 * library-specified string.
11 * The registry is accessed by calling GetNamedDSMSegment(). If a segment
12 * with the provided name does not yet exist, it is created and initialized
13 * with the provided init_callback callback function. Otherwise,
14 * GetNamedDSMSegment() simply ensures that the segment is attached to the
15 * current backend. This function guarantees that only one backend
16 * initializes the segment and that all other backends just attach it.
18 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
19 * Portions Copyright (c) 1994, Regents of the University of California
21 * IDENTIFICATION
22 * src/backend/storage/ipc/dsm_registry.c
24 *-------------------------------------------------------------------------
27 #include "postgres.h"
29 #include "lib/dshash.h"
30 #include "storage/dsm_registry.h"
31 #include "storage/lwlock.h"
32 #include "storage/shmem.h"
33 #include "utils/memutils.h"
35 typedef struct DSMRegistryCtxStruct
37 dsa_handle dsah;
38 dshash_table_handle dshh;
39 } DSMRegistryCtxStruct;
41 static DSMRegistryCtxStruct *DSMRegistryCtx;
43 typedef struct DSMRegistryEntry
45 char name[64];
46 dsm_handle handle;
47 size_t size;
48 } DSMRegistryEntry;
50 static const dshash_parameters dsh_params = {
51 offsetof(DSMRegistryEntry, handle),
52 sizeof(DSMRegistryEntry),
53 dshash_strcmp,
54 dshash_strhash,
55 dshash_strcpy,
56 LWTRANCHE_DSM_REGISTRY_HASH
59 static dsa_area *dsm_registry_dsa;
60 static dshash_table *dsm_registry_table;
62 Size
63 DSMRegistryShmemSize(void)
65 return MAXALIGN(sizeof(DSMRegistryCtxStruct));
68 void
69 DSMRegistryShmemInit(void)
71 bool found;
73 DSMRegistryCtx = (DSMRegistryCtxStruct *)
74 ShmemInitStruct("DSM Registry Data",
75 DSMRegistryShmemSize(),
76 &found);
78 if (!found)
80 DSMRegistryCtx->dsah = DSA_HANDLE_INVALID;
81 DSMRegistryCtx->dshh = DSHASH_HANDLE_INVALID;
86 * Initialize or attach to the dynamic shared hash table that stores the DSM
87 * registry entries, if not already done. This must be called before accessing
88 * the table.
90 static void
91 init_dsm_registry(void)
93 /* Quick exit if we already did this. */
94 if (dsm_registry_table)
95 return;
97 /* Otherwise, use a lock to ensure only one process creates the table. */
98 LWLockAcquire(DSMRegistryLock, LW_EXCLUSIVE);
100 if (DSMRegistryCtx->dshh == DSHASH_HANDLE_INVALID)
102 /* Initialize dynamic shared hash table for registry. */
103 dsm_registry_dsa = dsa_create(LWTRANCHE_DSM_REGISTRY_DSA);
104 dsa_pin(dsm_registry_dsa);
105 dsa_pin_mapping(dsm_registry_dsa);
106 dsm_registry_table = dshash_create(dsm_registry_dsa, &dsh_params, NULL);
108 /* Store handles in shared memory for other backends to use. */
109 DSMRegistryCtx->dsah = dsa_get_handle(dsm_registry_dsa);
110 DSMRegistryCtx->dshh = dshash_get_hash_table_handle(dsm_registry_table);
112 else
114 /* Attach to existing dynamic shared hash table. */
115 dsm_registry_dsa = dsa_attach(DSMRegistryCtx->dsah);
116 dsa_pin_mapping(dsm_registry_dsa);
117 dsm_registry_table = dshash_attach(dsm_registry_dsa, &dsh_params,
118 DSMRegistryCtx->dshh, NULL);
121 LWLockRelease(DSMRegistryLock);
125 * Initialize or attach a named DSM segment.
127 * This routine returns the address of the segment. init_callback is called to
128 * initialize the segment when it is first created.
130 void *
131 GetNamedDSMSegment(const char *name, size_t size,
132 void (*init_callback) (void *ptr), bool *found)
134 DSMRegistryEntry *entry;
135 MemoryContext oldcontext;
136 void *ret;
138 Assert(found);
140 if (!name || *name == '\0')
141 ereport(ERROR,
142 (errmsg("DSM segment name cannot be empty")));
144 if (strlen(name) >= offsetof(DSMRegistryEntry, handle))
145 ereport(ERROR,
146 (errmsg("DSM segment name too long")));
148 if (size == 0)
149 ereport(ERROR,
150 (errmsg("DSM segment size must be nonzero")));
152 /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
153 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
155 /* Connect to the registry. */
156 init_dsm_registry();
158 entry = dshash_find_or_insert(dsm_registry_table, name, found);
159 if (!(*found))
161 /* Initialize the segment. */
162 dsm_segment *seg = dsm_create(size, 0);
164 dsm_pin_segment(seg);
165 dsm_pin_mapping(seg);
166 entry->handle = dsm_segment_handle(seg);
167 entry->size = size;
168 ret = dsm_segment_address(seg);
170 if (init_callback)
171 (*init_callback) (ret);
173 else if (entry->size != size)
175 ereport(ERROR,
176 (errmsg("requested DSM segment size does not match size of "
177 "existing segment")));
179 else
181 dsm_segment *seg = dsm_find_mapping(entry->handle);
183 /* If the existing segment is not already attached, attach it now. */
184 if (seg == NULL)
186 seg = dsm_attach(entry->handle);
187 if (seg == NULL)
188 elog(ERROR, "could not map dynamic shared memory segment");
190 dsm_pin_mapping(seg);
193 ret = dsm_segment_address(seg);
196 dshash_release_lock(dsm_registry_table, entry);
197 MemoryContextSwitchTo(oldcontext);
199 return ret;