@@ -47,6 +47,12 @@ typedef struct DSMRegistryEntry
47
47
size_t size ;
48
48
} DSMRegistryEntry ;
49
49
50
+ typedef struct DSMDetachCallbackContext
51
+ {
52
+ void (* on_detach_callback ) (void * );
53
+ void * arg ;
54
+ } DSMDetachCallbackContext ;
55
+
50
56
static const dshash_parameters dsh_params = {
51
57
offsetof(DSMRegistryEntry , handle ),
52
58
sizeof (DSMRegistryEntry ),
@@ -121,6 +127,36 @@ init_dsm_registry(void)
121
127
LWLockRelease (DSMRegistryLock );
122
128
}
123
129
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
+
124
160
/*
125
161
* Initialize or attach a named DSM segment.
126
162
*
@@ -172,6 +208,8 @@ GetNamedDSMSegment(const char *name, size_t size,
172
208
}
173
209
else if (entry -> size != size )
174
210
{
211
+ dshash_release_lock (dsm_registry_table , entry );
212
+ MemoryContextSwitchTo (oldcontext );
175
213
ereport (ERROR ,
176
214
(errmsg ("requested DSM segment size does not match size of "
177
215
"existing segment" )));
@@ -185,8 +223,11 @@ GetNamedDSMSegment(const char *name, size_t size,
185
223
{
186
224
seg = dsm_attach (entry -> handle );
187
225
if (seg == NULL )
226
+ {
227
+ dshash_release_lock (dsm_registry_table , entry );
228
+ MemoryContextSwitchTo (oldcontext );
188
229
elog (ERROR , "could not map dynamic shared memory segment" );
189
-
230
+ }
190
231
dsm_pin_mapping (seg );
191
232
}
192
233
@@ -198,3 +239,127 @@ GetNamedDSMSegment(const char *name, size_t size,
198
239
199
240
return ret ;
200
241
}
242
+
243
+ /*
244
+ * Detach a named DSM segment
245
+ *
246
+ * This routine detaches the DSM segment. If the DSM segment was not attached
247
+ * by this process, then the routine just returns. on_detach_callback is passed
248
+ * on to dsm_segment by calling on_dsm_detach for the corresponding dsm_segment
249
+ * before actually detaching.
250
+ */
251
+ void
252
+ DetachNamedDSMSegment (const char * name , size_t size ,
253
+ void (* on_detach_callback ) (void * ), void * arg )
254
+ {
255
+ DSMRegistryEntry * entry ;
256
+ MemoryContext oldcontext ;
257
+
258
+ if (!name || * name == '\0' )
259
+ ereport (ERROR ,
260
+ (errmsg ("DSM segment name cannot be empty" )));
261
+
262
+ if (strlen (name ) >= offsetof(DSMRegistryEntry , handle ))
263
+ ereport (ERROR ,
264
+ (errmsg ("DSM segment name too long" )));
265
+
266
+ if (size == 0 )
267
+ ereport (ERROR ,
268
+ (errmsg ("DSM segment size must be nonzero" )));
269
+
270
+ /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
271
+ oldcontext = MemoryContextSwitchTo (TopMemoryContext );
272
+
273
+ /* Connect to the registry. */
274
+ init_dsm_registry ();
275
+
276
+ entry = dshash_find (dsm_registry_table , name , true);
277
+
278
+ if (entry == NULL )
279
+ {
280
+ MemoryContextSwitchTo (oldcontext );
281
+ ereport (ERROR ,
282
+ (errmsg ("cannot detach a DSM segment that does not exist" )));
283
+ }
284
+
285
+ if (entry -> size != size )
286
+ {
287
+ dshash_release_lock (dsm_registry_table , entry );
288
+ MemoryContextSwitchTo (oldcontext );
289
+ ereport (ERROR ,
290
+ (errmsg ("requested DSM segment size does not match size of "
291
+ "existing segment" )));
292
+ }
293
+
294
+ detach_dsm_segment (entry -> handle , on_detach_callback , arg );
295
+
296
+ dshash_release_lock (dsm_registry_table , entry );
297
+ MemoryContextSwitchTo (oldcontext );
298
+ }
299
+
300
+ /*
301
+ * Attempt to destroy a named DSM segment
302
+ *
303
+ * This routine attempts to destroy the DSM segment. We unpin the dsm_segment
304
+ * and delete the entry from dsm_registry_table. This may not destroy the
305
+ * dsm_segment instantly, but it would die out once all the other processes
306
+ * attached to this dsm_segment either exit or manually detach from the
307
+ * dsm_segment.
308
+ *
309
+ * Because we deleted the key from dsm_registry_table, calling
310
+ * GetNamedDSMSegment with the same key would result into creating a new
311
+ * dsm_segment instead of retrieving the old unpinned dsm_segment.
312
+ */
313
+ void
314
+ DestroyNamedDSMSegment (const char * name , size_t size ,
315
+ void (* on_detach_callback ) (void * ), void * arg )
316
+ {
317
+ DSMRegistryEntry * entry ;
318
+ MemoryContext oldcontext ;
319
+
320
+ if (!name || * name == '\0' )
321
+ ereport (ERROR ,
322
+ (errmsg ("DSM segment name cannot be empty" )));
323
+
324
+ if (strlen (name ) >= offsetof(DSMRegistryEntry , handle ))
325
+ ereport (ERROR ,
326
+ (errmsg ("DSM segment name too long" )));
327
+
328
+ if (size == 0 )
329
+ ereport (ERROR ,
330
+ (errmsg ("DSM segment size must be nonzero" )));
331
+
332
+ /* Be sure any local memory allocated by DSM/DSA routines is persistent. */
333
+ oldcontext = MemoryContextSwitchTo (TopMemoryContext );
334
+
335
+ /* Connect to the registry. */
336
+ init_dsm_registry ();
337
+
338
+ entry = dshash_find (dsm_registry_table , name , true);
339
+
340
+ if (entry == NULL )
341
+ {
342
+ MemoryContextSwitchTo (oldcontext );
343
+ ereport (ERROR ,
344
+ (errmsg ("cannot destroy a DSM segment that does not exist" )));
345
+ }
346
+
347
+ if (entry -> size != size )
348
+ {
349
+ dshash_release_lock (dsm_registry_table , entry );
350
+ MemoryContextSwitchTo (oldcontext );
351
+ ereport (ERROR ,
352
+ (errmsg ("requested DSM segment size does not match size of "
353
+ "existing segment" )));
354
+ }
355
+
356
+ detach_dsm_segment (entry -> handle , on_detach_callback , arg );
357
+ dsm_unpin_segment (entry -> handle );
358
+
359
+ /* dshash_delete_entry calls LWLockRelease internally. We shouldn't
360
+ * release lock twice */
361
+ dshash_delete_entry (dsm_registry_table , entry );
362
+ dshash_delete_key (dsm_registry_table , name );
363
+
364
+ MemoryContextSwitchTo (oldcontext );
365
+ }
0 commit comments