/src/postgres/src/backend/utils/adt/waitfuncs.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * waitfuncs.c |
4 | | * Functions for SQL access to syntheses of multiple contention types. |
5 | | * |
6 | | * Copyright (c) 2002-2025, PostgreSQL Global Development Group |
7 | | * |
8 | | * IDENTIFICATION |
9 | | * src/backend/utils/adt/waitfuncs.c |
10 | | * |
11 | | *------------------------------------------------------------------------- |
12 | | */ |
13 | | #include "postgres.h" |
14 | | |
15 | | #include "catalog/pg_type.h" |
16 | | #include "storage/predicate_internals.h" |
17 | | #include "storage/proc.h" |
18 | | #include "storage/procarray.h" |
19 | | #include "utils/array.h" |
20 | | #include "utils/fmgrprotos.h" |
21 | | #include "utils/wait_event.h" |
22 | | |
23 | 0 | #define UINT32_ACCESS_ONCE(var) ((uint32)(*((volatile uint32 *)&(var)))) |
24 | | |
25 | | |
26 | | /* |
27 | | * pg_isolation_test_session_is_blocked - support function for isolationtester |
28 | | * |
29 | | * Check if specified PID is blocked by any of the PIDs listed in the second |
30 | | * argument. Currently, this looks for blocking caused by waiting for |
31 | | * injection points, heavyweight locks, or safe snapshots. We ignore blockage |
32 | | * caused by PIDs not directly under the isolationtester's control, eg |
33 | | * autovacuum. |
34 | | * |
35 | | * This is an undocumented function intended for use by the isolation tester, |
36 | | * and may change in future releases as required for testing purposes. |
37 | | */ |
38 | | Datum |
39 | | pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS) |
40 | 0 | { |
41 | 0 | int blocked_pid = PG_GETARG_INT32(0); |
42 | 0 | ArrayType *interesting_pids_a = PG_GETARG_ARRAYTYPE_P(1); |
43 | 0 | PGPROC *proc; |
44 | 0 | const char *wait_event_type; |
45 | 0 | ArrayType *blocking_pids_a; |
46 | 0 | int32 *interesting_pids; |
47 | 0 | int32 *blocking_pids; |
48 | 0 | int num_interesting_pids; |
49 | 0 | int num_blocking_pids; |
50 | 0 | int dummy; |
51 | 0 | int i, |
52 | 0 | j; |
53 | | |
54 | | /* Check if blocked_pid is in an injection point. */ |
55 | 0 | proc = BackendPidGetProc(blocked_pid); |
56 | 0 | if (proc == NULL) |
57 | 0 | PG_RETURN_BOOL(false); /* session gone: definitely unblocked */ |
58 | 0 | wait_event_type = |
59 | 0 | pgstat_get_wait_event_type(UINT32_ACCESS_ONCE(proc->wait_event_info)); |
60 | 0 | if (wait_event_type && strcmp("InjectionPoint", wait_event_type) == 0) |
61 | 0 | PG_RETURN_BOOL(true); |
62 | | |
63 | | /* Validate the passed-in array */ |
64 | 0 | Assert(ARR_ELEMTYPE(interesting_pids_a) == INT4OID); |
65 | 0 | if (array_contains_nulls(interesting_pids_a)) |
66 | 0 | elog(ERROR, "array must not contain nulls"); |
67 | 0 | interesting_pids = (int32 *) ARR_DATA_PTR(interesting_pids_a); |
68 | 0 | num_interesting_pids = ArrayGetNItems(ARR_NDIM(interesting_pids_a), |
69 | 0 | ARR_DIMS(interesting_pids_a)); |
70 | | |
71 | | /* |
72 | | * Get the PIDs of all sessions blocking the given session's attempt to |
73 | | * acquire heavyweight locks. |
74 | | */ |
75 | 0 | blocking_pids_a = |
76 | 0 | DatumGetArrayTypeP(DirectFunctionCall1(pg_blocking_pids, blocked_pid)); |
77 | |
|
78 | 0 | Assert(ARR_ELEMTYPE(blocking_pids_a) == INT4OID); |
79 | 0 | Assert(!array_contains_nulls(blocking_pids_a)); |
80 | 0 | blocking_pids = (int32 *) ARR_DATA_PTR(blocking_pids_a); |
81 | 0 | num_blocking_pids = ArrayGetNItems(ARR_NDIM(blocking_pids_a), |
82 | 0 | ARR_DIMS(blocking_pids_a)); |
83 | | |
84 | | /* |
85 | | * Check if any of these are in the list of interesting PIDs, that being |
86 | | * the sessions that the isolation tester is running. We don't use |
87 | | * "arrayoverlaps" here, because it would lead to cache lookups and one of |
88 | | * our goals is to run quickly with debug_discard_caches > 0. We expect |
89 | | * blocking_pids to be usually empty and otherwise a very small number in |
90 | | * isolation tester cases, so make that the outer loop of a naive search |
91 | | * for a match. |
92 | | */ |
93 | 0 | for (i = 0; i < num_blocking_pids; i++) |
94 | 0 | for (j = 0; j < num_interesting_pids; j++) |
95 | 0 | { |
96 | 0 | if (blocking_pids[i] == interesting_pids[j]) |
97 | 0 | PG_RETURN_BOOL(true); |
98 | 0 | } |
99 | | |
100 | | /* |
101 | | * Check if blocked_pid is waiting for a safe snapshot. We could in |
102 | | * theory check the resulting array of blocker PIDs against the |
103 | | * interesting PIDs list, but since there is no danger of autovacuum |
104 | | * blocking GetSafeSnapshot there seems to be no point in expending cycles |
105 | | * on allocating a buffer and searching for overlap; so it's presently |
106 | | * sufficient for the isolation tester's purposes to use a single element |
107 | | * buffer and check if the number of safe snapshot blockers is non-zero. |
108 | | */ |
109 | 0 | if (GetSafeSnapshotBlockingPids(blocked_pid, &dummy, 1) > 0) |
110 | 0 | PG_RETURN_BOOL(true); |
111 | | |
112 | 0 | PG_RETURN_BOOL(false); |
113 | 0 | } |