Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+14
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

+85-36
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

+3
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

+32
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

+10
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

+17
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

+7
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)