18
18
#include "postgres.h"
19
19
20
20
#include "fmgr.h"
21
+ #include "miscadmin.h"
21
22
#include "storage/condition_variable.h"
22
23
#include "storage/dsm_registry.h"
24
+ #include "storage/ipc.h"
23
25
#include "storage/lwlock.h"
24
26
#include "storage/shmem.h"
25
27
#include "utils/builtins.h"
@@ -31,6 +33,23 @@ PG_MODULE_MAGIC;
31
33
/* Maximum number of waits usable in injection points at once */
32
34
#define INJ_MAX_WAIT 8
33
35
#define INJ_NAME_MAXLEN 64
36
+ #define INJ_MAX_CONDITION 4
37
+
38
+ /*
39
+ * Conditions related to injection points. This tracks in shared memory the
40
+ * runtime conditions under which an injection point is allowed to run.
41
+ *
42
+ * If more types of runtime conditions need to be tracked, this structure
43
+ * should be expanded.
44
+ */
45
+ typedef struct InjectionPointCondition
46
+ {
47
+ /* Name of the injection point related to this condition */
48
+ char name [INJ_NAME_MAXLEN ];
49
+
50
+ /* ID of the process where the injection point is allowed to run */
51
+ int pid ;
52
+ } InjectionPointCondition ;
34
53
35
54
/* Shared state information for injection points. */
36
55
typedef struct InjectionPointSharedState
@@ -46,6 +65,9 @@ typedef struct InjectionPointSharedState
46
65
47
66
/* Condition variable used for waits and wakeups */
48
67
ConditionVariable wait_point ;
68
+
69
+ /* Conditions to run an injection point */
70
+ InjectionPointCondition conditions [INJ_MAX_CONDITION ];
49
71
} InjectionPointSharedState ;
50
72
51
73
/* Pointer to shared-memory state. */
@@ -55,6 +77,8 @@ extern PGDLLEXPORT void injection_error(const char *name);
55
77
extern PGDLLEXPORT void injection_notice (const char * name );
56
78
extern PGDLLEXPORT void injection_wait (const char * name );
57
79
80
+ /* track if injection points attached in this process are linked to it */
81
+ static bool injection_point_local = false;
58
82
59
83
/*
60
84
* Callback for shared memory area initialization.
@@ -67,6 +91,7 @@ injection_point_init_state(void *ptr)
67
91
SpinLockInit (& state -> lock );
68
92
memset (state -> wait_counts , 0 , sizeof (state -> wait_counts ));
69
93
memset (state -> name , 0 , sizeof (state -> name ));
94
+ memset (state -> conditions , 0 , sizeof (state -> conditions ));
70
95
ConditionVariableInit (& state -> wait_point );
71
96
}
72
97
@@ -87,16 +112,92 @@ injection_init_shmem(void)
87
112
& found );
88
113
}
89
114
115
+ /*
116
+ * Check runtime conditions associated to an injection point.
117
+ *
118
+ * Returns true if the named injection point is allowed to run, and false
119
+ * otherwise. Multiple conditions can be associated to a single injection
120
+ * point, so check them all.
121
+ */
122
+ static bool
123
+ injection_point_allowed (const char * name )
124
+ {
125
+ bool result = true;
126
+
127
+ if (inj_state == NULL )
128
+ injection_init_shmem ();
129
+
130
+ SpinLockAcquire (& inj_state -> lock );
131
+
132
+ for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
133
+ {
134
+ InjectionPointCondition * condition = & inj_state -> conditions [i ];
135
+
136
+ if (strcmp (condition -> name , name ) == 0 )
137
+ {
138
+ /*
139
+ * Check if this injection point is allowed to run in this
140
+ * process.
141
+ */
142
+ if (MyProcPid != condition -> pid )
143
+ {
144
+ result = false;
145
+ break ;
146
+ }
147
+ }
148
+ }
149
+
150
+ SpinLockRelease (& inj_state -> lock );
151
+
152
+ return result ;
153
+ }
154
+
155
+ /*
156
+ * before_shmem_exit callback to remove injection points linked to a
157
+ * specific process.
158
+ */
159
+ static void
160
+ injection_points_cleanup (int code , Datum arg )
161
+ {
162
+ /* Leave if nothing is tracked locally */
163
+ if (!injection_point_local )
164
+ return ;
165
+
166
+ SpinLockAcquire (& inj_state -> lock );
167
+ for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
168
+ {
169
+ InjectionPointCondition * condition = & inj_state -> conditions [i ];
170
+
171
+ if (condition -> name [0 ] == '\0' )
172
+ continue ;
173
+
174
+ if (condition -> pid != MyProcPid )
175
+ continue ;
176
+
177
+ /* Detach the injection point and unregister condition */
178
+ InjectionPointDetach (condition -> name );
179
+ condition -> name [0 ] = '\0' ;
180
+ condition -> pid = 0 ;
181
+ }
182
+ SpinLockRelease (& inj_state -> lock );
183
+ }
184
+
90
185
/* Set of callbacks available to be attached to an injection point. */
91
186
void
92
187
injection_error (const char * name )
93
188
{
189
+ if (!injection_point_allowed (name ))
190
+ return ;
191
+
94
192
elog (ERROR , "error triggered for injection point %s" , name );
95
193
}
96
194
97
195
void
98
196
injection_notice (const char * name )
99
197
{
198
+ if (!injection_point_allowed (name ))
199
+ return ;
200
+
100
201
elog (NOTICE , "notice triggered for injection point %s" , name );
101
202
}
102
203
@@ -111,6 +212,9 @@ injection_wait(const char *name)
111
212
if (inj_state == NULL )
112
213
injection_init_shmem ();
113
214
215
+ if (!injection_point_allowed (name ))
216
+ return ;
217
+
114
218
/*
115
219
* Use the injection point name for this custom wait event. Note that
116
220
* this custom wait event name is not released, but we don't care much for
@@ -182,6 +286,35 @@ injection_points_attach(PG_FUNCTION_ARGS)
182
286
183
287
InjectionPointAttach (name , "injection_points" , function );
184
288
289
+ if (injection_point_local )
290
+ {
291
+ int index = -1 ;
292
+
293
+ /*
294
+ * Register runtime condition to link this injection point to the
295
+ * current process.
296
+ */
297
+ SpinLockAcquire (& inj_state -> lock );
298
+ for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
299
+ {
300
+ InjectionPointCondition * condition = & inj_state -> conditions [i ];
301
+
302
+ if (condition -> name [0 ] == '\0' )
303
+ {
304
+ index = i ;
305
+ strlcpy (condition -> name , name , INJ_NAME_MAXLEN );
306
+ condition -> pid = MyProcPid ;
307
+ break ;
308
+ }
309
+ }
310
+ SpinLockRelease (& inj_state -> lock );
311
+
312
+ if (index < 0 )
313
+ elog (FATAL ,
314
+ "could not find free slot for condition of injection point %s" ,
315
+ name );
316
+ }
317
+
185
318
PG_RETURN_VOID ();
186
319
}
187
320
@@ -235,6 +368,32 @@ injection_points_wakeup(PG_FUNCTION_ARGS)
235
368
PG_RETURN_VOID ();
236
369
}
237
370
371
+ /*
372
+ * injection_points_set_local
373
+ *
374
+ * Track if any injection point created in this process ought to run only
375
+ * in this process. Such injection points are detached automatically when
376
+ * this process exits. This is useful to make test suites concurrent-safe.
377
+ */
378
+ PG_FUNCTION_INFO_V1 (injection_points_set_local );
379
+ Datum
380
+ injection_points_set_local (PG_FUNCTION_ARGS )
381
+ {
382
+ /* Enable flag to add a runtime condition based on this process ID */
383
+ injection_point_local = true;
384
+
385
+ if (inj_state == NULL )
386
+ injection_init_shmem ();
387
+
388
+ /*
389
+ * Register a before_shmem_exit callback to remove any injection points
390
+ * linked to this process.
391
+ */
392
+ before_shmem_exit (injection_points_cleanup , (Datum ) 0 );
393
+
394
+ PG_RETURN_VOID ();
395
+ }
396
+
238
397
/*
239
398
* SQL function for dropping an injection point.
240
399
*/
@@ -246,5 +405,22 @@ injection_points_detach(PG_FUNCTION_ARGS)
246
405
247
406
InjectionPointDetach (name );
248
407
408
+ if (inj_state == NULL )
409
+ injection_init_shmem ();
410
+
411
+ /* Clean up any conditions associated to this injection point */
412
+ SpinLockAcquire (& inj_state -> lock );
413
+ for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
414
+ {
415
+ InjectionPointCondition * condition = & inj_state -> conditions [i ];
416
+
417
+ if (strcmp (condition -> name , name ) == 0 )
418
+ {
419
+ condition -> pid = 0 ;
420
+ condition -> name [0 ] = '\0' ;
421
+ }
422
+ }
423
+ SpinLockRelease (& inj_state -> lock );
424
+
249
425
PG_RETURN_VOID ();
250
426
}
0 commit comments