Skip to content

Commit 238f95f

Browse files
author
Commitfest Bot
committed
[CF 5654] v4 - dsm_registry: Add detach and destroy features
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5654 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CAAdDe3PQiQGyeRDgWKynpKXVi7nAAWP+o_up8Ji4utKcNA79rw@mail.gmail.com Author(s): Sungwoo Chang
2 parents 7374b3a + 4f21111 commit 238f95f

File tree

8 files changed

+253
-1
lines changed

8 files changed

+253
-1
lines changed

src/backend/storage/ipc/dsm.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,21 @@ dsm_segment_handle(dsm_segment *seg)
11251125
return seg->handle;
11261126
}
11271127

1128+
uint32_t dsm_segment_refcnt(dsm_segment *seg)
1129+
{
1130+
uint32_t refcnt = 0;
1131+
1132+
if (seg->control_slot != INVALID_CONTROL_SLOT)
1133+
{
1134+
LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
1135+
Assert(dsm_control->item[seg->control_slot].handle == seg->handle);
1136+
refcnt = dsm_control->item[seg->control_slot].refcnt;
1137+
LWLockRelease(DynamicSharedMemoryControlLock);
1138+
}
1139+
1140+
return refcnt;
1141+
}
1142+
11281143
/*
11291144
* Register an on-detach callback for a dynamic shared memory segment.
11301145
*/

src/backend/storage/ipc/dsm_registry.c

Lines changed: 181 additions & 1 deletion
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,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+
}

src/include/storage/dsm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ extern dsm_segment *dsm_find_mapping(dsm_handle handle);
4949
extern void *dsm_segment_address(dsm_segment *seg);
5050
extern Size dsm_segment_map_length(dsm_segment *seg);
5151
extern dsm_handle dsm_segment_handle(dsm_segment *seg);
52+
extern uint32_t dsm_segment_refcnt(dsm_segment *seg);
5253

5354
/* Cleanup hooks. */
5455
typedef void (*on_dsm_detach_callback) (dsm_segment *, Datum arg);

src/include/storage/dsm_registry.h

Lines changed: 8 additions & 0 deletions
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

Lines changed: 12 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 28 additions & 0 deletions
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_INT32(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)