Skip to content

Commit 4b21100

Browse files
committed
Support loading of injection points
This can be used to load an injection point and prewarm the backend-level cache before running it, to avoid issues if the point cannot be loaded due to restrictions in the code path where it would be run, like a critical section where no memory allocation can happen (load_external_function() can do allocations when expanding a library name). Tests can use a macro called INJECTION_POINT_LOAD() to load an injection point. The test module injection_points gains some tests, and a SQL function able to load an injection point. Based on a request from Andrey Borodin, who has implemented a test for multixacts requiring this facility. Reviewed-by: Andrey Borodin Discussion: https://postgr.es/m/ZkrBE1e2q2wGvsoN@paquier.xyz
1 parent 98347b5 commit 4b21100

File tree

7 files changed

+168
-36
lines changed

7 files changed

+168
-36
lines changed

doc/src/sgml/xfunc.sgml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,6 +3618,20 @@ INJECTION_POINT(name);
36183618
their own code using the same macro.
36193619
</para>
36203620

3621+
<para>
3622+
An injection point with a given <literal>name</literal> can be loaded
3623+
using macro:
3624+
<programlisting>
3625+
INJECTION_POINT_LOAD(name);
3626+
</programlisting>
3627+
3628+
This will load the injection point callback into the process cache,
3629+
doing all memory allocations at this stage without running the callback.
3630+
This is useful when an injection point is attached in a critical section
3631+
where no memory can be allocated: load the injection point outside the
3632+
critical section, then run it in the critical section.
3633+
</para>
3634+
36213635
<para>
36223636
Add-ins can attach callbacks to an already-declared injection point by
36233637
calling:

src/backend/utils/misc/injection_point.c

Lines changed: 85 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,47 @@ injection_point_cache_remove(const char *name)
129129
(void) hash_search(InjectionPointCache, name, HASH_REMOVE, NULL);
130130
}
131131

132+
/*
133+
* injection_point_cache_load
134+
*
135+
* Load an injection point into the local cache.
136+
*/
137+
static void
138+
injection_point_cache_load(InjectionPointEntry *entry_by_name)
139+
{
140+
char path[MAXPGPATH];
141+
void *injection_callback_local;
142+
143+
snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
144+
entry_by_name->library, DLSUFFIX);
145+
146+
if (!pg_file_exists(path))
147+
elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
148+
path, entry_by_name->name);
149+
150+
injection_callback_local = (void *)
151+
load_external_function(path, entry_by_name->function, false, NULL);
152+
153+
if (injection_callback_local == NULL)
154+
elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"",
155+
entry_by_name->function, path, entry_by_name->name);
156+
157+
/* add it to the local cache when found */
158+
injection_point_cache_add(entry_by_name->name, injection_callback_local,
159+
entry_by_name->private_data);
160+
}
161+
132162
/*
133163
* injection_point_cache_get
134164
*
135165
* Retrieve an injection point from the local cache, if any.
136166
*/
137-
static InjectionPointCallback
138-
injection_point_cache_get(const char *name, const void **private_data)
167+
static InjectionPointCacheEntry *
168+
injection_point_cache_get(const char *name)
139169
{
140170
bool found;
141171
InjectionPointCacheEntry *entry;
142172

143-
if (private_data)
144-
*private_data = NULL;
145-
146173
/* no callback if no cache yet */
147174
if (InjectionPointCache == NULL)
148175
return NULL;
@@ -151,11 +178,7 @@ injection_point_cache_get(const char *name, const void **private_data)
151178
hash_search(InjectionPointCache, name, HASH_FIND, &found);
152179

153180
if (found)
154-
{
155-
if (private_data)
156-
*private_data = entry->private_data;
157-
return entry->callback;
158-
}
181+
return entry;
159182

160183
return NULL;
161184
}
@@ -278,6 +301,52 @@ InjectionPointDetach(const char *name)
278301
#endif
279302
}
280303

304+
/*
305+
* Load an injection point into the local cache.
306+
*
307+
* This is useful to be able to load an injection point before running it,
308+
* especially if the injection point is called in a code path where memory
309+
* allocations cannot happen, like critical sections.
310+
*/
311+
void
312+
InjectionPointLoad(const char *name)
313+
{
314+
#ifdef USE_INJECTION_POINTS
315+
InjectionPointEntry *entry_by_name;
316+
bool found;
317+
318+
LWLockAcquire(InjectionPointLock, LW_SHARED);
319+
entry_by_name = (InjectionPointEntry *)
320+
hash_search(InjectionPointHash, name,
321+
HASH_FIND, &found);
322+
323+
/*
324+
* If not found, do nothing and remove it from the local cache if it
325+
* existed there.
326+
*/
327+
if (!found)
328+
{
329+
injection_point_cache_remove(name);
330+
LWLockRelease(InjectionPointLock);
331+
return;
332+
}
333+
334+
/* Check first the local cache, and leave if this entry exists. */
335+
if (injection_point_cache_get(name) != NULL)
336+
{
337+
LWLockRelease(InjectionPointLock);
338+
return;
339+
}
340+
341+
/* Nothing? Then load it and leave */
342+
injection_point_cache_load(entry_by_name);
343+
344+
LWLockRelease(InjectionPointLock);
345+
#else
346+
elog(ERROR, "Injection points are not supported by this build");
347+
#endif
348+
}
349+
281350
/*
282351
* Execute an injection point, if defined.
283352
*
@@ -290,8 +359,7 @@ InjectionPointRun(const char *name)
290359
#ifdef USE_INJECTION_POINTS
291360
InjectionPointEntry *entry_by_name;
292361
bool found;
293-
InjectionPointCallback injection_callback;
294-
const void *private_data;
362+
InjectionPointCacheEntry *cache_entry;
295363

296364
LWLockAcquire(InjectionPointLock, LW_SHARED);
297365
entry_by_name = (InjectionPointEntry *)
@@ -313,37 +381,18 @@ InjectionPointRun(const char *name)
313381
* Check if the callback exists in the local cache, to avoid unnecessary
314382
* external loads.
315383
*/
316-
if (injection_point_cache_get(name, NULL) == NULL)
384+
if (injection_point_cache_get(name) == NULL)
317385
{
318-
char path[MAXPGPATH];
319-
InjectionPointCallback injection_callback_local;
320-
321-
/* not found in local cache, so load and register */
322-
snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
323-
entry_by_name->library, DLSUFFIX);
324-
325-
if (!pg_file_exists(path))
326-
elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
327-
path, name);
328-
329-
injection_callback_local = (InjectionPointCallback)
330-
load_external_function(path, entry_by_name->function, false, NULL);
331-
332-
if (injection_callback_local == NULL)
333-
elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"",
334-
entry_by_name->function, path, name);
335-
336-
/* add it to the local cache when found */
337-
injection_point_cache_add(name, injection_callback_local,
338-
entry_by_name->private_data);
386+
/* not found in local cache, so load and register it */
387+
injection_point_cache_load(entry_by_name);
339388
}
340389

341390
/* Now loaded, so get it. */
342-
injection_callback = injection_point_cache_get(name, &private_data);
391+
cache_entry = injection_point_cache_get(name);
343392

344393
LWLockRelease(InjectionPointLock);
345394

346-
injection_callback(name, private_data);
395+
cache_entry->callback(name, cache_entry->private_data);
347396
#else
348397
elog(ERROR, "Injection points are not supported by this build");
349398
#endif

src/include/utils/injection_point.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
* Injections points require --enable-injection-points.
1616
*/
1717
#ifdef USE_INJECTION_POINTS
18+
#define INJECTION_POINT_LOAD(name) InjectionPointLoad(name)
1819
#define INJECTION_POINT(name) InjectionPointRun(name)
1920
#else
21+
#define INJECTION_POINT_LOAD(name) ((void) name)
2022
#define INJECTION_POINT(name) ((void) name)
2123
#endif
2224

@@ -34,6 +36,7 @@ extern void InjectionPointAttach(const char *name,
3436
const char *function,
3537
const void *private_data,
3638
int private_data_size);
39+
extern void InjectionPointLoad(const char *name);
3740
extern void InjectionPointRun(const char *name);
3841
extern bool InjectionPointDetach(const char *name);
3942

src/test/modules/injection_points/expected/injection_points.out

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,38 @@ SELECT injection_points_detach('TestInjectionLog2');
128128

129129
(1 row)
130130

131+
-- Loading
132+
SELECT injection_points_load('TestInjectionLogLoad'); -- nothing
133+
injection_points_load
134+
-----------------------
135+
136+
(1 row)
137+
138+
SELECT injection_points_attach('TestInjectionLogLoad', 'notice');
139+
injection_points_attach
140+
-------------------------
141+
142+
(1 row)
143+
144+
SELECT injection_points_load('TestInjectionLogLoad'); -- nothing happens
145+
injection_points_load
146+
-----------------------
147+
148+
(1 row)
149+
150+
SELECT injection_points_run('TestInjectionLogLoad'); -- runs from cache
151+
NOTICE: notice triggered for injection point TestInjectionLogLoad
152+
injection_points_run
153+
----------------------
154+
155+
(1 row)
156+
157+
SELECT injection_points_detach('TestInjectionLogLoad');
158+
injection_points_detach
159+
-------------------------
160+
161+
(1 row)
162+
131163
-- Runtime conditions
132164
SELECT injection_points_attach('TestConditionError', 'error');
133165
injection_points_attach

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ RETURNS void
1414
AS 'MODULE_PATHNAME', 'injection_points_attach'
1515
LANGUAGE C STRICT PARALLEL UNSAFE;
1616

17+
--
18+
-- injection_points_load()
19+
--
20+
-- Load an injection point already attached.
21+
--
22+
CREATE FUNCTION injection_points_load(IN point_name TEXT)
23+
RETURNS void
24+
AS 'MODULE_PATHNAME', 'injection_points_load'
25+
LANGUAGE C STRICT PARALLEL UNSAFE;
26+
1727
--
1828
-- injection_points_run()
1929
--

src/test/modules/injection_points/injection_points.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,23 @@ injection_points_attach(PG_FUNCTION_ARGS)
302302
PG_RETURN_VOID();
303303
}
304304

305+
/*
306+
* SQL function for loading an injection point.
307+
*/
308+
PG_FUNCTION_INFO_V1(injection_points_load);
309+
Datum
310+
injection_points_load(PG_FUNCTION_ARGS)
311+
{
312+
char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
313+
314+
if (inj_state == NULL)
315+
injection_init_shmem();
316+
317+
INJECTION_POINT_LOAD(name);
318+
319+
PG_RETURN_VOID();
320+
}
321+
305322
/*
306323
* SQL function for triggering an injection point.
307324
*/

src/test/modules/injection_points/sql/injection_points.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ SELECT injection_points_detach('TestInjectionLog'); -- fails
4141
SELECT injection_points_run('TestInjectionLog2'); -- notice
4242
SELECT injection_points_detach('TestInjectionLog2');
4343

44+
-- Loading
45+
SELECT injection_points_load('TestInjectionLogLoad'); -- nothing
46+
SELECT injection_points_attach('TestInjectionLogLoad', 'notice');
47+
SELECT injection_points_load('TestInjectionLogLoad'); -- nothing happens
48+
SELECT injection_points_run('TestInjectionLogLoad'); -- runs from cache
49+
SELECT injection_points_detach('TestInjectionLogLoad');
50+
4451
-- Runtime conditions
4552
SELECT injection_points_attach('TestConditionError', 'error');
4653
-- Any follow-up injection point attached will be local to this process.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy