1919
2020#include "fmgr.h"
2121#include "miscadmin.h"
22+ #include "nodes/pg_list.h"
23+ #include "nodes/value.h"
2224#include "storage/condition_variable.h"
2325#include "storage/dsm_registry.h"
2426#include "storage/ipc.h"
2527#include "storage/lwlock.h"
2628#include "storage/shmem.h"
2729#include "utils/builtins.h"
2830#include "utils/injection_point.h"
31+ #include "utils/memutils.h"
2932#include "utils/wait_event.h"
3033
3134PG_MODULE_MAGIC ;
3235
3336/* Maximum number of waits usable in injection points at once */
3437#define INJ_MAX_WAIT 8
3538#define INJ_NAME_MAXLEN 64
36- #define INJ_MAX_CONDITION 4
3739
3840/*
3941 * Conditions related to injection points. This tracks in shared memory the
40- * runtime conditions under which an injection point is allowed to run.
42+ * runtime conditions under which an injection point is allowed to run,
43+ * stored as private_data when an injection point is attached, and passed as
44+ * argument to the callback.
4145 *
4246 * If more types of runtime conditions need to be tracked, this structure
4347 * should be expanded.
4448 */
49+ typedef enum InjectionPointConditionType
50+ {
51+ INJ_CONDITION_ALWAYS = 0 , /* always run */
52+ INJ_CONDITION_PID , /* PID restriction */
53+ } InjectionPointConditionType ;
54+
4555typedef struct InjectionPointCondition
4656{
47- /* Name of the injection point related to this condition */
48- char name [ INJ_NAME_MAXLEN ] ;
57+ /* Type of the condition */
58+ InjectionPointConditionType type ;
4959
5060 /* ID of the process where the injection point is allowed to run */
5161 int pid ;
5262} InjectionPointCondition ;
5363
64+ /*
65+ * List of injection points stored in TopMemoryContext attached
66+ * locally to this process.
67+ */
68+ static List * inj_list_local = NIL ;
69+
5470/* Shared state information for injection points. */
5571typedef struct InjectionPointSharedState
5672{
@@ -65,9 +81,6 @@ typedef struct InjectionPointSharedState
6581
6682 /* Condition variable used for waits and wakeups */
6783 ConditionVariable wait_point ;
68-
69- /* Conditions to run an injection point */
70- InjectionPointCondition conditions [INJ_MAX_CONDITION ];
7184} InjectionPointSharedState ;
7285
7386/* Pointer to shared-memory state. */
@@ -94,7 +107,6 @@ injection_point_init_state(void *ptr)
94107 SpinLockInit (& state -> lock );
95108 memset (state -> wait_counts , 0 , sizeof (state -> wait_counts ));
96109 memset (state -> name , 0 , sizeof (state -> name ));
97- memset (state -> conditions , 0 , sizeof (state -> conditions ));
98110 ConditionVariableInit (& state -> wait_point );
99111}
100112
@@ -119,39 +131,23 @@ injection_init_shmem(void)
119131 * Check runtime conditions associated to an injection point.
120132 *
121133 * Returns true if the named injection point is allowed to run, and false
122- * otherwise. Multiple conditions can be associated to a single injection
123- * point, so check them all.
134+ * otherwise.
124135 */
125136static bool
126- injection_point_allowed (const char * name )
137+ injection_point_allowed (InjectionPointCondition * condition )
127138{
128139 bool result = true;
129140
130- if (inj_state == NULL )
131- injection_init_shmem ();
132-
133- SpinLockAcquire (& inj_state -> lock );
134-
135- for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
141+ switch (condition -> type )
136142 {
137- InjectionPointCondition * condition = & inj_state -> conditions [i ];
138-
139- if (strcmp (condition -> name , name ) == 0 )
140- {
141- /*
142- * Check if this injection point is allowed to run in this
143- * process.
144- */
143+ case INJ_CONDITION_PID :
145144 if (MyProcPid != condition -> pid )
146- {
147145 result = false;
148- break ;
149- }
150- }
146+ break ;
147+ case INJ_CONDITION_ALWAYS :
148+ break ;
151149 }
152150
153- SpinLockRelease (& inj_state -> lock );
154-
155151 return result ;
156152}
157153
@@ -162,61 +158,28 @@ injection_point_allowed(const char *name)
162158static void
163159injection_points_cleanup (int code , Datum arg )
164160{
165- char names [INJ_MAX_CONDITION ][INJ_NAME_MAXLEN ] = {0 };
166- int count = 0 ;
161+ ListCell * lc ;
167162
168163 /* Leave if nothing is tracked locally */
169164 if (!injection_point_local )
170165 return ;
171166
172- /*
173- * This is done in three steps: detect the points to detach, detach them
174- * and release their conditions.
175- */
176- SpinLockAcquire (& inj_state -> lock );
177- for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
178- {
179- InjectionPointCondition * condition = & inj_state -> conditions [i ];
180-
181- if (condition -> name [0 ] == '\0' )
182- continue ;
183-
184- if (condition -> pid != MyProcPid )
185- continue ;
186-
187- /* Extract the point name to detach */
188- strlcpy (names [count ], condition -> name , INJ_NAME_MAXLEN );
189- count ++ ;
190- }
191- SpinLockRelease (& inj_state -> lock );
192-
193- /* Detach, without holding the spinlock */
194- for (int i = 0 ; i < count ; i ++ )
195- (void ) InjectionPointDetach (names [i ]);
196-
197- /* Clear all the conditions */
198- SpinLockAcquire (& inj_state -> lock );
199- for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
167+ /* Detach all the local points */
168+ foreach (lc , inj_list_local )
200169 {
201- InjectionPointCondition * condition = & inj_state -> conditions [ i ] ;
170+ char * name = strVal ( lfirst ( lc )) ;
202171
203- if (condition -> name [0 ] == '\0' )
204- continue ;
205-
206- if (condition -> pid != MyProcPid )
207- continue ;
208-
209- condition -> name [0 ] = '\0' ;
210- condition -> pid = 0 ;
172+ (void ) InjectionPointDetach (name );
211173 }
212- SpinLockRelease (& inj_state -> lock );
213174}
214175
215176/* Set of callbacks available to be attached to an injection point. */
216177void
217178injection_error (const char * name , const void * private_data )
218179{
219- if (!injection_point_allowed (name ))
180+ InjectionPointCondition * condition = (InjectionPointCondition * ) private_data ;
181+
182+ if (!injection_point_allowed (condition ))
220183 return ;
221184
222185 elog (ERROR , "error triggered for injection point %s" , name );
@@ -225,7 +188,9 @@ injection_error(const char *name, const void *private_data)
225188void
226189injection_notice (const char * name , const void * private_data )
227190{
228- if (!injection_point_allowed (name ))
191+ InjectionPointCondition * condition = (InjectionPointCondition * ) private_data ;
192+
193+ if (!injection_point_allowed (condition ))
229194 return ;
230195
231196 elog (NOTICE , "notice triggered for injection point %s" , name );
@@ -238,11 +203,12 @@ injection_wait(const char *name, const void *private_data)
238203 uint32 old_wait_counts = 0 ;
239204 int index = -1 ;
240205 uint32 injection_wait_event = 0 ;
206+ InjectionPointCondition * condition = (InjectionPointCondition * ) private_data ;
241207
242208 if (inj_state == NULL )
243209 injection_init_shmem ();
244210
245- if (!injection_point_allowed (name ))
211+ if (!injection_point_allowed (condition ))
246212 return ;
247213
248214 /*
@@ -304,6 +270,7 @@ injection_points_attach(PG_FUNCTION_ARGS)
304270 char * name = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
305271 char * action = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
306272 char * function ;
273+ InjectionPointCondition condition = {0 };
307274
308275 if (strcmp (action , "error" ) == 0 )
309276 function = "injection_error" ;
@@ -314,37 +281,24 @@ injection_points_attach(PG_FUNCTION_ARGS)
314281 else
315282 elog (ERROR , "incorrect action \"%s\" for injection point creation" , action );
316283
317- InjectionPointAttach (name , "injection_points" , function , NULL , 0 );
318-
319284 if (injection_point_local )
320285 {
321- int index = -1 ;
286+ condition .type = INJ_CONDITION_PID ;
287+ condition .pid = MyProcPid ;
288+ }
322289
323- /*
324- * Register runtime condition to link this injection point to the
325- * current process.
326- */
327- SpinLockAcquire (& inj_state -> lock );
328- for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
329- {
330- InjectionPointCondition * condition = & inj_state -> conditions [i ];
331-
332- if (condition -> name [0 ] == '\0' )
333- {
334- index = i ;
335- strlcpy (condition -> name , name , INJ_NAME_MAXLEN );
336- condition -> pid = MyProcPid ;
337- break ;
338- }
339- }
340- SpinLockRelease (& inj_state -> lock );
290+ InjectionPointAttach (name , "injection_points" , function , & condition ,
291+ sizeof (InjectionPointCondition ));
341292
342- if (index < 0 )
343- elog (FATAL ,
344- "could not find free slot for condition of injection point %s" ,
345- name );
346- }
293+ if (injection_point_local )
294+ {
295+ MemoryContext oldctx ;
347296
297+ /* Local injection point, so track it for automated cleanup */
298+ oldctx = MemoryContextSwitchTo (TopMemoryContext );
299+ inj_list_local = lappend (inj_list_local , makeString (pstrdup (name )));
300+ MemoryContextSwitchTo (oldctx );
301+ }
348302 PG_RETURN_VOID ();
349303}
350304
@@ -436,22 +390,15 @@ injection_points_detach(PG_FUNCTION_ARGS)
436390 if (!InjectionPointDetach (name ))
437391 elog (ERROR , "could not detach injection point \"%s\"" , name );
438392
439- if (inj_state == NULL )
440- injection_init_shmem ();
441-
442- /* Clean up any conditions associated to this injection point */
443- SpinLockAcquire (& inj_state -> lock );
444- for (int i = 0 ; i < INJ_MAX_CONDITION ; i ++ )
393+ /* Remove point from local list, if required */
394+ if (inj_list_local != NIL )
445395 {
446- InjectionPointCondition * condition = & inj_state -> conditions [ i ] ;
396+ MemoryContext oldctx ;
447397
448- if (strcmp (condition -> name , name ) == 0 )
449- {
450- condition -> pid = 0 ;
451- condition -> name [0 ] = '\0' ;
452- }
398+ oldctx = MemoryContextSwitchTo (TopMemoryContext );
399+ inj_list_local = list_delete (inj_list_local , makeString (name ));
400+ MemoryContextSwitchTo (oldctx );
453401 }
454- SpinLockRelease (& inj_state -> lock );
455402
456403 PG_RETURN_VOID ();
457404}
0 commit comments