@@ -47,6 +47,12 @@ typedef struct DSMRegistryEntry
4747 size_t size ;
4848} DSMRegistryEntry ;
4949
50+ typedef struct DSMDetachCallbackContext
51+ {
52+ void (* on_detach_callback ) (void * );
53+ void * arg ;
54+ } DSMDetachCallbackContext ;
55+
5056static const dshash_parameters dsh_params = {
5157 offsetof(DSMRegistryEntry , handle ),
5258 sizeof (DSMRegistryEntry ),
@@ -121,6 +127,43 @@ init_dsm_registry(void)
121127 LWLockRelease (DSMRegistryLock );
122128}
123129
130+ static void
131+ call_on_detach_callback (dsm_segment * seg , Datum arg )
132+ {
133+ char * ptr = DatumGetPointer (arg );
134+ DSMDetachCallbackContext * cb_ctx = (DSMDetachCallbackContext * )ptr ;
135+ cb_ctx -> on_detach_callback (cb_ctx -> arg );
136+ }
137+
138+ static void
139+ detach_dsm_segment (dsm_handle handle , void (* detach_cb ) (void * ), void * arg )
140+ {
141+ dsm_segment * seg = dsm_find_mapping (handle );
142+
143+ /* Detach the segment only if it is already attached */
144+ if (seg != NULL )
145+ {
146+ if (detach_cb != NULL )
147+ {
148+ DSMDetachCallbackContext * cb_ctx = palloc (sizeof (DSMDetachCallbackContext ));
149+ cb_ctx -> on_detach_callback = detach_cb ;
150+ cb_ctx -> arg = arg ;
151+
152+ on_dsm_detach (seg , call_on_detach_callback , PointerGetDatum (cb_ctx ));
153+ }
154+
155+ dsm_unpin_mapping (seg );
156+ dsm_detach (seg );
157+ }
158+ }
159+
160+ static bool
161+ is_dsm_segment_in_use (dsm_handle handle )
162+ {
163+ dsm_segment * seg = dsm_find_mapping (handle );
164+ return seg != NULL && dsm_segment_refcnt (seg ) > 2 ;
165+ }
166+
124167/*
125168 * Initialize or attach a named DSM segment.
126169 *
@@ -172,6 +215,8 @@ GetNamedDSMSegment(const char *name, size_t size,
172215 }
173216 else if (entry -> size != size )
174217 {
218+ dshash_release_lock (dsm_registry_table , entry );
219+ MemoryContextSwitchTo (oldcontext );
175220 ereport (ERROR ,
176221 (errmsg ("requested DSM segment size does not match size of "
177222 "existing segment" )));
@@ -185,8 +230,11 @@ GetNamedDSMSegment(const char *name, size_t size,
185230 {
186231 seg = dsm_attach (entry -> handle );
187232 if (seg == NULL )
233+ {
234+ dshash_release_lock (dsm_registry_table , entry );
235+ MemoryContextSwitchTo (oldcontext );
188236 elog (ERROR , "could not map dynamic shared memory segment" );
189-
237+ }
190238 dsm_pin_mapping (seg );
191239 }
192240
@@ -198,3 +246,135 @@ GetNamedDSMSegment(const char *name, size_t size,
198246
199247 return ret ;
200248}
249+
250+ /*
251+ * Detach a named DSM segment
252+ *
253+ * This routine detaches the DSM segment. If the DSM segment was not attached
254+ * by this process, then the routine just returns. on_detach_callback is passed
255+ * on to dsm_segment by calling on_dsm_detach for the corresponding dsm_segment
256+ * before actually detaching.
257+ */
258+ void
259+ DetachNamedDSMSegment (const char * name , size_t size ,
260+ void (* on_detach_callback ) (void * ), void * arg )
261+ {
262+ DSMRegistryEntry * entry ;
263+ MemoryContext oldcontext ;
264+
265+ if (!name || * name == '\0' )
266+ ereport (ERROR ,
267+ (errmsg ("DSM segment name cannot be empty" )));
268+
269+ if (strlen (name ) >= offsetof(DSMRegistryEntry , handle ))
270+ ereport (ERROR ,
271+ (errmsg ("DSM segment name too long" )));
272+
273+ if (size == 0 )
274+ ereport (ERROR ,
275+ (errmsg ("DSM segment size must be nonzero" )));
276+
277+ /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
278+ oldcontext = MemoryContextSwitchTo (TopMemoryContext );
279+
280+ /* Connect to the registry. */
281+ init_dsm_registry ();
282+
283+ entry = dshash_find (dsm_registry_table , name , true);
284+
285+ if (entry == NULL )
286+ {
287+ MemoryContextSwitchTo (oldcontext );
288+ ereport (ERROR ,
289+ (errmsg ("cannot detach a DSM segment that does not exist" )));
290+ }
291+
292+ if (entry -> size != size )
293+ {
294+ dshash_release_lock (dsm_registry_table , entry );
295+ MemoryContextSwitchTo (oldcontext );
296+ ereport (ERROR ,
297+ (errmsg ("requested DSM segment size does not match size of "
298+ "existing segment" )));
299+ }
300+
301+ detach_dsm_segment (entry -> handle , on_detach_callback , arg );
302+
303+ dshash_release_lock (dsm_registry_table , entry );
304+ MemoryContextSwitchTo (oldcontext );
305+ }
306+
307+ /*
308+ * Attempt to destroy a named DSM segment
309+ *
310+ * This routine attempts to destroy the DSM segment. We unpin the dsm_segment
311+ * and delete the entry from dsm_registry_table. This may not destroy the
312+ * dsm_segment instantly, but it would die out once all the other processes
313+ * attached to this dsm_segment either exit or manually detach from the
314+ * dsm_segment.
315+ *
316+ * Because we deleted the key from dsm_registry_table, calling
317+ * GetNamedDSMSegment with the same key would result into creating a new
318+ * dsm_segment instead of retrieving the old unpinned dsm_segment.
319+ */
320+ void
321+ DestroyNamedDSMSegment (const char * name , size_t size ,
322+ void (* on_detach_callback ) (void * ), void * arg )
323+ {
324+ DSMRegistryEntry * entry ;
325+ MemoryContext oldcontext ;
326+
327+ if (!name || * name == '\0' )
328+ ereport (ERROR ,
329+ (errmsg ("DSM segment name cannot be empty" )));
330+
331+ if (strlen (name ) >= offsetof(DSMRegistryEntry , handle ))
332+ ereport (ERROR ,
333+ (errmsg ("DSM segment name too long" )));
334+
335+ if (size == 0 )
336+ ereport (ERROR ,
337+ (errmsg ("DSM segment size must be nonzero" )));
338+
339+ /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
340+ oldcontext = MemoryContextSwitchTo (TopMemoryContext );
341+
342+ /* Connect to the registry. */
343+ init_dsm_registry ();
344+
345+ entry = dshash_find (dsm_registry_table , name , true);
346+
347+ if (entry == NULL )
348+ {
349+ MemoryContextSwitchTo (oldcontext );
350+ ereport (ERROR ,
351+ (errmsg ("cannot destroy a DSM segment that does not exist" )));
352+ }
353+
354+ if (entry -> size != size )
355+ {
356+ dshash_release_lock (dsm_registry_table , entry );
357+ MemoryContextSwitchTo (oldcontext );
358+ ereport (ERROR ,
359+ (errmsg ("requested DSM segment size does not match size of "
360+ "existing segment" )));
361+ }
362+
363+ if (is_dsm_segment_in_use (entry -> handle ))
364+ {
365+ dshash_release_lock (dsm_registry_table , entry );
366+ MemoryContextSwitchTo (oldcontext );
367+ ereport (ERROR ,
368+ (errmsg ("cannot destroy a DSM segment that is still in use" )));
369+ }
370+
371+ detach_dsm_segment (entry -> handle , on_detach_callback , arg );
372+ dsm_unpin_segment (entry -> handle );
373+
374+ /* dshash_delete_entry calls LWLockRelease internally. We shouldn't
375+ * release lock twice */
376+ dshash_delete_entry (dsm_registry_table , entry );
377+ dshash_delete_key (dsm_registry_table , name );
378+
379+ MemoryContextSwitchTo (oldcontext );
380+ }
0 commit comments