Skip to content

Commit 55fd8f4

Browse files
swchangdevCommitfest Bot
authored and
Commitfest Bot
committed
Add detach and destroy feature to dsm_registry
1 parent 78231ba commit 55fd8f4

File tree

6 files changed

+222
-1
lines changed

6 files changed

+222
-1
lines changed

src/backend/storage/ipc/dsm_registry.c

+166-1
Original file line numberDiff line numberDiff line change
@@ -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+
5056
static const dshash_parameters dsh_params = {
5157
offsetof(DSMRegistryEntry, handle),
5258
sizeof(DSMRegistryEntry),
@@ -121,6 +127,36 @@ 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+
124160
/*
125161
* Initialize or attach a named DSM segment.
126162
*
@@ -172,6 +208,8 @@ GetNamedDSMSegment(const char *name, size_t size,
172208
}
173209
else if (entry->size != size)
174210
{
211+
dshash_release_lock(dsm_registry_table, entry);
212+
MemoryContextSwitchTo(oldcontext);
175213
ereport(ERROR,
176214
(errmsg("requested DSM segment size does not match size of "
177215
"existing segment")));
@@ -185,8 +223,11 @@ GetNamedDSMSegment(const char *name, size_t size,
185223
{
186224
seg = dsm_attach(entry->handle);
187225
if (seg == NULL)
226+
{
227+
dshash_release_lock(dsm_registry_table, entry);
228+
MemoryContextSwitchTo(oldcontext);
188229
elog(ERROR, "could not map dynamic shared memory segment");
189-
230+
}
190231
dsm_pin_mapping(seg);
191232
}
192233

@@ -198,3 +239,127 @@ GetNamedDSMSegment(const char *name, size_t size,
198239

199240
return ret;
200241
}
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+
}

src/include/storage/dsm_registry.h

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ extern void *GetNamedDSMSegment(const char *name, size_t size,
1717
void (*init_callback) (void *ptr),
1818
bool *found);
1919

20+
extern void DetachNamedDSMSegment(const char *name, size_t size,
21+
void (*on_detach_callback) (void *),
22+
void *arg);
23+
24+
extern void DestroyNamedDSMSegment(const char *name, size_t size,
25+
void (*on_detach_callback) (void *),
26+
void *arg);
27+
2028
extern Size DSMRegistryShmemSize(void);
2129
extern void DSMRegistryShmemInit(void);
2230

src/test/modules/test_dsm_registry/expected/test_dsm_registry.out

+12
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ SELECT get_val_in_shmem();
1212
1236
1313
(1 row)
1414

15+
SELECT detach_from_tdr_segment();
16+
detach_from_tdr_segment
17+
-------------------------
18+
t
19+
(1 row)
20+
21+
SELECT destroy_tdr_segment();
22+
destroy_tdr_segment
23+
---------------------
24+
25+
(1 row)
26+

src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql

+2
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ CREATE EXTENSION test_dsm_registry;
22
SELECT set_val_in_shmem(1236);
33
\c
44
SELECT get_val_in_shmem();
5+
SELECT detach_from_tdr_segment();
6+
SELECT destroy_tdr_segment();

src/test/modules/test_dsm_registry/test_dsm_registry--1.0.sql

+6
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ CREATE FUNCTION set_val_in_shmem(val INT) RETURNS VOID
88

99
CREATE FUNCTION get_val_in_shmem() RETURNS INT
1010
AS 'MODULE_PATHNAME' LANGUAGE C;
11+
12+
CREATE FUNCTION detach_from_tdr_segment() RETURNS BOOL
13+
AS 'MODULE_PATHNAME' LANGUAGE C;
14+
15+
CREATE FUNCTION destroy_tdr_segment() RETURNS VOID
16+
AS 'MODULE_PATHNAME' LANGUAGE C;

src/test/modules/test_dsm_registry/test_dsm_registry.c

+28
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ tdr_init_shmem(void *ptr)
3535
state->val = 0;
3636
}
3737

38+
static void
39+
reset_tdr_state_to_null(void *arg)
40+
{
41+
tdr_state = NULL;
42+
}
43+
3844
static void
3945
tdr_attach_shmem(void)
4046
{
@@ -74,3 +80,25 @@ get_val_in_shmem(PG_FUNCTION_ARGS)
7480

7581
PG_RETURN_UINT32(ret);
7682
}
83+
84+
PG_FUNCTION_INFO_V1(detach_from_tdr_segment);
85+
Datum
86+
detach_from_tdr_segment(PG_FUNCTION_ARGS)
87+
{
88+
DetachNamedDSMSegment("test_dsm_registry",
89+
sizeof(TestDSMRegistryStruct),
90+
reset_tdr_state_to_null, NULL);
91+
92+
PG_RETURN_BOOL(tdr_state == NULL);
93+
}
94+
95+
PG_FUNCTION_INFO_V1(destroy_tdr_segment);
96+
Datum
97+
destroy_tdr_segment(PG_FUNCTION_ARGS)
98+
{
99+
DestroyNamedDSMSegment("test_dsm_registry",
100+
sizeof(TestDSMRegistryStruct),
101+
NULL, NULL);
102+
103+
PG_RETURN_VOID();
104+
}

0 commit comments

Comments
 (0)