Skip to content

Commit 33181b4

Browse files
committed
Introduce private data area for injection points
This commit extends the backend-side infrastructure of injection points so as it becomes possible to register some input data when attaching a point. This private data can be registered with the function name and the library name of the callback when attaching a point, then it is given as input argument to the callback. This gives the possibility for modules to pass down custom data at runtime when attaching a point without managing that internally, in a manner consistent with the callback entry retrieved from the hash shmem table storing the injection point data. InjectionPointAttach() gains two arguments, to be able to define the private data contents and its size. A follow-up commit will rely on this infrastructure to close a race condition with the injection point detach in the module injection_points. While on it, this changes InjectionPointDetach() to return a boolean, returning false if a point cannot be detached. This has been mentioned by Noah as useful when it comes to implement more complex tests with concurrent point detach, solid with the automatic detach done for local points in the test module. Documentation is adjusted in consequence. Per discussion with Noah Misch. Reviewed-by: Noah Misch Discussion: https://postgr.es/m/20240509031553.47@rfd.leadboat.com
1 parent 407e0b0 commit 33181b4

File tree

5 files changed

+74
-28
lines changed

5 files changed

+74
-28
lines changed

doc/src/sgml/xfunc.sgml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3624,20 +3624,24 @@ INJECTION_POINT(name);
36243624
<programlisting>
36253625
extern void InjectionPointAttach(const char *name,
36263626
const char *library,
3627-
const char *function);
3627+
const char *function,
3628+
const void *private_data,
3629+
int private_data_size);
36283630
</programlisting>
36293631

36303632
<literal>name</literal> is the name of the injection point, which when
36313633
reached during execution will execute the <literal>function</literal>
3632-
loaded from <literal>library</literal>.
3634+
loaded from <literal>library</literal>. <literal>private_data</literal>
3635+
is a private area of data of size <literal>private_data_size</literal>
3636+
given as argument to the callback when executed.
36333637
</para>
36343638

36353639
<para>
36363640
Here is an example of callback for
36373641
<literal>InjectionPointCallback</literal>:
36383642
<programlisting>
36393643
static void
3640-
custom_injection_callback(const char *name)
3644+
custom_injection_callback(const char *name, const void *private_data)
36413645
{
36423646
elog(NOTICE, "%s: executed custom callback", name);
36433647
}
@@ -3650,8 +3654,10 @@ custom_injection_callback(const char *name)
36503654
<para>
36513655
Optionally, it is possible to detach an injection point by calling:
36523656
<programlisting>
3653-
extern void InjectionPointDetach(const char *name);
3657+
extern bool InjectionPointDetach(const char *name);
36543658
</programlisting>
3659+
On success, <literal>true</literal> is returned, <literal>false</literal>
3660+
otherwise.
36553661
</para>
36563662

36573663
<para>

src/backend/utils/misc/injection_point.c

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,20 @@ static HTAB *InjectionPointHash; /* find points from names */
4242
#define INJ_NAME_MAXLEN 64
4343
#define INJ_LIB_MAXLEN 128
4444
#define INJ_FUNC_MAXLEN 128
45+
#define INJ_PRIVATE_MAXLEN 1024
4546

4647
/* Single injection point stored in InjectionPointHash */
4748
typedef struct InjectionPointEntry
4849
{
4950
char name[INJ_NAME_MAXLEN]; /* hash key */
5051
char library[INJ_LIB_MAXLEN]; /* library */
5152
char function[INJ_FUNC_MAXLEN]; /* function */
53+
54+
/*
55+
* Opaque data area that modules can use to pass some custom data to
56+
* callbacks, registered when attached.
57+
*/
58+
char private_data[INJ_PRIVATE_MAXLEN];
5259
} InjectionPointEntry;
5360

5461
#define INJECTION_POINT_HASH_INIT_SIZE 16
@@ -61,6 +68,7 @@ typedef struct InjectionPointEntry
6168
typedef struct InjectionPointCacheEntry
6269
{
6370
char name[INJ_NAME_MAXLEN];
71+
char private_data[INJ_PRIVATE_MAXLEN];
6472
InjectionPointCallback callback;
6573
} InjectionPointCacheEntry;
6674

@@ -73,7 +81,8 @@ static HTAB *InjectionPointCache = NULL;
7381
*/
7482
static void
7583
injection_point_cache_add(const char *name,
76-
InjectionPointCallback callback)
84+
InjectionPointCallback callback,
85+
const void *private_data)
7786
{
7887
InjectionPointCacheEntry *entry;
7988
bool found;
@@ -99,6 +108,8 @@ injection_point_cache_add(const char *name,
99108
Assert(!found);
100109
strlcpy(entry->name, name, sizeof(entry->name));
101110
entry->callback = callback;
111+
if (private_data != NULL)
112+
memcpy(entry->private_data, private_data, INJ_PRIVATE_MAXLEN);
102113
}
103114

104115
/*
@@ -124,11 +135,14 @@ injection_point_cache_remove(const char *name)
124135
* Retrieve an injection point from the local cache, if any.
125136
*/
126137
static InjectionPointCallback
127-
injection_point_cache_get(const char *name)
138+
injection_point_cache_get(const char *name, const void **private_data)
128139
{
129140
bool found;
130141
InjectionPointCacheEntry *entry;
131142

143+
if (private_data)
144+
*private_data = NULL;
145+
132146
/* no callback if no cache yet */
133147
if (InjectionPointCache == NULL)
134148
return NULL;
@@ -137,7 +151,11 @@ injection_point_cache_get(const char *name)
137151
hash_search(InjectionPointCache, name, HASH_FIND, &found);
138152

139153
if (found)
154+
{
155+
if (private_data)
156+
*private_data = entry->private_data;
140157
return entry->callback;
158+
}
141159

142160
return NULL;
143161
}
@@ -186,7 +204,9 @@ InjectionPointShmemInit(void)
186204
void
187205
InjectionPointAttach(const char *name,
188206
const char *library,
189-
const char *function)
207+
const char *function,
208+
const void *private_data,
209+
int private_data_size)
190210
{
191211
#ifdef USE_INJECTION_POINTS
192212
InjectionPointEntry *entry_by_name;
@@ -201,6 +221,9 @@ InjectionPointAttach(const char *name,
201221
if (strlen(function) >= INJ_FUNC_MAXLEN)
202222
elog(ERROR, "injection point function %s too long (maximum of %u)",
203223
function, INJ_FUNC_MAXLEN);
224+
if (private_data_size >= INJ_PRIVATE_MAXLEN)
225+
elog(ERROR, "injection point data too long (maximum of %u)",
226+
INJ_PRIVATE_MAXLEN);
204227

205228
/*
206229
* Allocate and register a new injection point. A new point should not
@@ -223,6 +246,8 @@ InjectionPointAttach(const char *name,
223246
entry_by_name->library[INJ_LIB_MAXLEN - 1] = '\0';
224247
strlcpy(entry_by_name->function, function, sizeof(entry_by_name->function));
225248
entry_by_name->function[INJ_FUNC_MAXLEN - 1] = '\0';
249+
if (private_data != NULL)
250+
memcpy(entry_by_name->private_data, private_data, private_data_size);
226251

227252
LWLockRelease(InjectionPointLock);
228253

@@ -233,8 +258,10 @@ InjectionPointAttach(const char *name,
233258

234259
/*
235260
* Detach an existing injection point.
261+
*
262+
* Returns true if the injection point was detached, false otherwise.
236263
*/
237-
void
264+
bool
238265
InjectionPointDetach(const char *name)
239266
{
240267
#ifdef USE_INJECTION_POINTS
@@ -245,10 +272,12 @@ InjectionPointDetach(const char *name)
245272
LWLockRelease(InjectionPointLock);
246273

247274
if (!found)
248-
elog(ERROR, "injection point \"%s\" not found", name);
275+
return false;
249276

277+
return true;
250278
#else
251279
elog(ERROR, "Injection points are not supported by this build");
280+
return true; /* silence compiler */
252281
#endif
253282
}
254283

@@ -265,6 +294,7 @@ InjectionPointRun(const char *name)
265294
InjectionPointEntry *entry_by_name;
266295
bool found;
267296
InjectionPointCallback injection_callback;
297+
const void *private_data;
268298

269299
LWLockAcquire(InjectionPointLock, LW_SHARED);
270300
entry_by_name = (InjectionPointEntry *)
@@ -286,10 +316,10 @@ InjectionPointRun(const char *name)
286316
* Check if the callback exists in the local cache, to avoid unnecessary
287317
* external loads.
288318
*/
289-
injection_callback = injection_point_cache_get(name);
290-
if (injection_callback == NULL)
319+
if (injection_point_cache_get(name, NULL) == NULL)
291320
{
292321
char path[MAXPGPATH];
322+
InjectionPointCallback injection_callback_local;
293323

294324
/* not found in local cache, so load and register */
295325
snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
@@ -299,18 +329,21 @@ InjectionPointRun(const char *name)
299329
elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
300330
path, name);
301331

302-
injection_callback = (InjectionPointCallback)
332+
injection_callback_local = (InjectionPointCallback)
303333
load_external_function(path, entry_by_name->function, false, NULL);
304334

305-
if (injection_callback == NULL)
335+
if (injection_callback_local == NULL)
306336
elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"",
307337
entry_by_name->function, path, name);
308338

309339
/* add it to the local cache when found */
310-
injection_point_cache_add(name, injection_callback);
340+
injection_point_cache_add(name, injection_callback_local,
341+
entry_by_name->private_data);
311342
}
312343

313-
injection_callback(name);
344+
/* Now loaded, so get it. */
345+
injection_callback = injection_point_cache_get(name, &private_data);
346+
injection_callback(name, private_data);
314347
#else
315348
elog(ERROR, "Injection points are not supported by this build");
316349
#endif

src/include/utils/injection_point.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,18 @@
2323
/*
2424
* Typedef for callback function launched by an injection point.
2525
*/
26-
typedef void (*InjectionPointCallback) (const char *name);
26+
typedef void (*InjectionPointCallback) (const char *name,
27+
const void *private_data);
2728

2829
extern Size InjectionPointShmemSize(void);
2930
extern void InjectionPointShmemInit(void);
3031

3132
extern void InjectionPointAttach(const char *name,
3233
const char *library,
33-
const char *function);
34+
const char *function,
35+
const void *private_data,
36+
int private_data_size);
3437
extern void InjectionPointRun(const char *name);
35-
extern void InjectionPointDetach(const char *name);
38+
extern bool InjectionPointDetach(const char *name);
3639

3740
#endif /* INJECTION_POINT_H */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ NOTICE: notice triggered for injection point TestInjectionLog2
114114
(1 row)
115115

116116
SELECT injection_points_detach('TestInjectionLog'); -- fails
117-
ERROR: injection point "TestInjectionLog" not found
117+
ERROR: could not detach injection point "TestInjectionLog"
118118
SELECT injection_points_run('TestInjectionLog2'); -- notice
119119
NOTICE: notice triggered for injection point TestInjectionLog2
120120
injection_points_run

src/test/modules/injection_points/injection_points.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ typedef struct InjectionPointSharedState
7373
/* Pointer to shared-memory state. */
7474
static InjectionPointSharedState *inj_state = NULL;
7575

76-
extern PGDLLEXPORT void injection_error(const char *name);
77-
extern PGDLLEXPORT void injection_notice(const char *name);
78-
extern PGDLLEXPORT void injection_wait(const char *name);
76+
extern PGDLLEXPORT void injection_error(const char *name,
77+
const void *private_data);
78+
extern PGDLLEXPORT void injection_notice(const char *name,
79+
const void *private_data);
80+
extern PGDLLEXPORT void injection_wait(const char *name,
81+
const void *private_data);
7982

8083
/* track if injection points attached in this process are linked to it */
8184
static bool injection_point_local = false;
@@ -189,7 +192,7 @@ injection_points_cleanup(int code, Datum arg)
189192

190193
/* Detach, without holding the spinlock */
191194
for (int i = 0; i < count; i++)
192-
InjectionPointDetach(names[i]);
195+
(void) InjectionPointDetach(names[i]);
193196

194197
/* Clear all the conditions */
195198
SpinLockAcquire(&inj_state->lock);
@@ -211,7 +214,7 @@ injection_points_cleanup(int code, Datum arg)
211214

212215
/* Set of callbacks available to be attached to an injection point. */
213216
void
214-
injection_error(const char *name)
217+
injection_error(const char *name, const void *private_data)
215218
{
216219
if (!injection_point_allowed(name))
217220
return;
@@ -220,7 +223,7 @@ injection_error(const char *name)
220223
}
221224

222225
void
223-
injection_notice(const char *name)
226+
injection_notice(const char *name, const void *private_data)
224227
{
225228
if (!injection_point_allowed(name))
226229
return;
@@ -230,7 +233,7 @@ injection_notice(const char *name)
230233

231234
/* Wait on a condition variable, awaken by injection_points_wakeup() */
232235
void
233-
injection_wait(const char *name)
236+
injection_wait(const char *name, const void *private_data)
234237
{
235238
uint32 old_wait_counts = 0;
236239
int index = -1;
@@ -311,7 +314,7 @@ injection_points_attach(PG_FUNCTION_ARGS)
311314
else
312315
elog(ERROR, "incorrect action \"%s\" for injection point creation", action);
313316

314-
InjectionPointAttach(name, "injection_points", function);
317+
InjectionPointAttach(name, "injection_points", function, NULL, 0);
315318

316319
if (injection_point_local)
317320
{
@@ -430,7 +433,8 @@ injection_points_detach(PG_FUNCTION_ARGS)
430433
{
431434
char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
432435

433-
InjectionPointDetach(name);
436+
if (!InjectionPointDetach(name))
437+
elog(ERROR, "could not detach injection point \"%s\"", name);
434438

435439
if (inj_state == NULL)
436440
injection_init_shmem();

0 commit comments

Comments
 (0)