1 /*-------------------------------------------------------------------------
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
22 * src/backend/storage/ipc/dsm_registry.c
24 *-------------------------------------------------------------------------
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
38 dshash_table_handle dshh
;
39 } DSMRegistryCtxStruct
;
41 static DSMRegistryCtxStruct
*DSMRegistryCtx
;
43 typedef struct DSMRegistryEntry
50 static const dshash_parameters dsh_params
= {
51 offsetof(DSMRegistryEntry
, handle
),
52 sizeof(DSMRegistryEntry
),
56 LWTRANCHE_DSM_REGISTRY_HASH
59 static dsa_area
*dsm_registry_dsa
;
60 static dshash_table
*dsm_registry_table
;
63 DSMRegistryShmemSize(void)
65 return MAXALIGN(sizeof(DSMRegistryCtxStruct
));
69 DSMRegistryShmemInit(void)
73 DSMRegistryCtx
= (DSMRegistryCtxStruct
*)
74 ShmemInitStruct("DSM Registry Data",
75 DSMRegistryShmemSize(),
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
91 init_dsm_registry(void)
93 /* Quick exit if we already did this. */
94 if (dsm_registry_table
)
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
);
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.
131 GetNamedDSMSegment(const char *name
, size_t size
,
132 void (*init_callback
) (void *ptr
), bool *found
)
134 DSMRegistryEntry
*entry
;
135 MemoryContext oldcontext
;
140 if (!name
|| *name
== '\0')
142 (errmsg("DSM segment name cannot be empty")));
144 if (strlen(name
) >= offsetof(DSMRegistryEntry
, handle
))
146 (errmsg("DSM segment name too long")));
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. */
158 entry
= dshash_find_or_insert(dsm_registry_table
, name
, 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
);
168 ret
= dsm_segment_address(seg
);
171 (*init_callback
) (ret
);
173 else if (entry
->size
!= size
)
176 (errmsg("requested DSM segment size does not match size of "
177 "existing segment")));
181 dsm_segment
*seg
= dsm_find_mapping(entry
->handle
);
183 /* If the existing segment is not already attached, attach it now. */
186 seg
= dsm_attach(entry
->handle
);
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
);