Line data Source code
1 : /*--------------------------------------------------------------------------
2 : *
3 : * test_resowner_many.c
4 : * Test ResourceOwner functionality with lots of resources
5 : *
6 : * Copyright (c) 2022-2024, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/test/modules/test_resowner/test_resowner_many.c
10 : *
11 : * -------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
15 : #include "fmgr.h"
16 : #include "lib/ilist.h"
17 : #include "utils/memutils.h"
18 : #include "utils/resowner.h"
19 :
20 : /*
21 : * Define a custom resource type to use in the test. The resource being
22 : * tracked is a palloc'd ManyTestResource struct.
23 : *
24 : * To cross-check that the ResourceOwner calls the callback functions
25 : * correctly, we keep track of the remembered resources ourselves in a linked
26 : * list, and also keep counters of how many times the callback functions have
27 : * been called.
28 : */
29 : typedef struct
30 : {
31 : ResourceOwnerDesc desc;
32 : int nremembered;
33 : int nforgotten;
34 : int nreleased;
35 : int nleaked;
36 :
37 : dlist_head current_resources;
38 : } ManyTestResourceKind;
39 :
40 : typedef struct
41 : {
42 : ManyTestResourceKind *kind;
43 : dlist_node node;
44 : } ManyTestResource;
45 :
46 : /*
47 : * Current release phase, and priority of last call to the release callback.
48 : * This is used to check that the resources are released in correct order.
49 : */
50 : static ResourceReleasePhase current_release_phase;
51 : static uint32 last_release_priority = 0;
52 :
53 : /* prototypes for local functions */
54 : static void ReleaseManyTestResource(Datum res);
55 : static char *PrintManyTest(Datum res);
56 : static void InitManyTestResourceKind(ManyTestResourceKind *kind, char *name,
57 : ResourceReleasePhase phase, uint32 priority);
58 : static void RememberManyTestResources(ResourceOwner owner,
59 : ManyTestResourceKind *kinds, int nkinds,
60 : int nresources);
61 : static void ForgetManyTestResources(ResourceOwner owner,
62 : ManyTestResourceKind *kinds, int nkinds,
63 : int nresources);
64 : static int GetTotalResourceCount(ManyTestResourceKind *kinds, int nkinds);
65 :
66 : /* ResourceOwner callback */
67 : static void
68 398000 : ReleaseManyTestResource(Datum res)
69 : {
70 398000 : ManyTestResource *mres = (ManyTestResource *) DatumGetPointer(res);
71 :
72 398000 : elog(DEBUG1, "releasing resource %p from %s", mres, mres->kind->desc.name);
73 : Assert(last_release_priority <= mres->kind->desc.release_priority);
74 :
75 398000 : dlist_delete(&mres->node);
76 398000 : mres->kind->nreleased++;
77 398000 : last_release_priority = mres->kind->desc.release_priority;
78 398000 : pfree(mres);
79 398000 : }
80 :
81 : /* ResourceOwner callback */
82 : static char *
83 0 : PrintManyTest(Datum res)
84 : {
85 0 : ManyTestResource *mres = (ManyTestResource *) DatumGetPointer(res);
86 :
87 : /*
88 : * XXX: we assume that the DebugPrint function is called once for each
89 : * leaked resource, and that there are no other callers.
90 : */
91 0 : mres->kind->nleaked++;
92 :
93 0 : return psprintf("many-test resource from %s", mres->kind->desc.name);
94 : }
95 :
96 : static void
97 12 : InitManyTestResourceKind(ManyTestResourceKind *kind, char *name,
98 : ResourceReleasePhase phase, uint32 priority)
99 : {
100 12 : kind->desc.name = name;
101 12 : kind->desc.release_phase = phase;
102 12 : kind->desc.release_priority = priority;
103 12 : kind->desc.ReleaseResource = ReleaseManyTestResource;
104 12 : kind->desc.DebugPrint = PrintManyTest;
105 12 : kind->nremembered = 0;
106 12 : kind->nforgotten = 0;
107 12 : kind->nreleased = 0;
108 12 : kind->nleaked = 0;
109 12 : dlist_init(&kind->current_resources);
110 12 : }
111 :
112 : /*
113 : * Remember 'nresources' resources. The resources are remembered in round
114 : * robin fashion with the kinds from 'kinds' array.
115 : */
116 : static void
117 4 : RememberManyTestResources(ResourceOwner owner,
118 : ManyTestResourceKind *kinds, int nkinds,
119 : int nresources)
120 : {
121 4 : int kind_idx = 0;
122 :
123 400004 : for (int i = 0; i < nresources; i++)
124 : {
125 400000 : ManyTestResource *mres = palloc(sizeof(ManyTestResource));
126 :
127 400000 : mres->kind = &kinds[kind_idx];
128 400000 : dlist_node_init(&mres->node);
129 :
130 400000 : ResourceOwnerEnlarge(owner);
131 400000 : ResourceOwnerRemember(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
132 400000 : kinds[kind_idx].nremembered++;
133 400000 : dlist_push_tail(&kinds[kind_idx].current_resources, &mres->node);
134 :
135 400000 : elog(DEBUG1, "remembered resource %p from %s", mres, mres->kind->desc.name);
136 :
137 400000 : kind_idx = (kind_idx + 1) % nkinds;
138 : }
139 4 : }
140 :
141 : /*
142 : * Forget 'nresources' resources, in round robin fashion from 'kinds'.
143 : */
144 : static void
145 4 : ForgetManyTestResources(ResourceOwner owner,
146 : ManyTestResourceKind *kinds, int nkinds,
147 : int nresources)
148 : {
149 4 : int kind_idx = 0;
150 : int ntotal;
151 :
152 4 : ntotal = GetTotalResourceCount(kinds, nkinds);
153 4 : if (ntotal < nresources)
154 0 : elog(PANIC, "cannot free %d resources, only %d remembered", nresources, ntotal);
155 :
156 2004 : for (int i = 0; i < nresources; i++)
157 : {
158 2000 : bool found = false;
159 :
160 2000 : for (int j = 0; j < nkinds; j++)
161 : {
162 2000 : kind_idx = (kind_idx + 1) % nkinds;
163 2000 : if (!dlist_is_empty(&kinds[kind_idx].current_resources))
164 : {
165 2000 : ManyTestResource *mres = dlist_head_element(ManyTestResource, node, &kinds[kind_idx].current_resources);
166 :
167 2000 : ResourceOwnerForget(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
168 2000 : kinds[kind_idx].nforgotten++;
169 2000 : dlist_delete(&mres->node);
170 2000 : pfree(mres);
171 :
172 2000 : found = true;
173 2000 : break;
174 : }
175 : }
176 2000 : if (!found)
177 0 : elog(ERROR, "could not find a test resource to forget");
178 : }
179 4 : }
180 :
181 : /*
182 : * Get total number of currently active resources among 'kinds'.
183 : */
184 : static int
185 4 : GetTotalResourceCount(ManyTestResourceKind *kinds, int nkinds)
186 : {
187 4 : int ntotal = 0;
188 :
189 16 : for (int i = 0; i < nkinds; i++)
190 12 : ntotal += kinds[i].nremembered - kinds[i].nforgotten - kinds[i].nreleased;
191 :
192 4 : return ntotal;
193 : }
194 :
195 : /*
196 : * Remember lots of resources, belonging to 'nkinds' different resource types
197 : * with different priorities. Then forget some of them, and finally, release
198 : * the resource owner. We use a custom resource type that performs various
199 : * sanity checks to verify that all the resources are released, and in the
200 : * correct order.
201 : */
202 4 : PG_FUNCTION_INFO_V1(test_resowner_many);
203 : Datum
204 2 : test_resowner_many(PG_FUNCTION_ARGS)
205 : {
206 2 : int32 nkinds = PG_GETARG_INT32(0);
207 2 : int32 nremember_bl = PG_GETARG_INT32(1);
208 2 : int32 nforget_bl = PG_GETARG_INT32(2);
209 2 : int32 nremember_al = PG_GETARG_INT32(3);
210 2 : int32 nforget_al = PG_GETARG_INT32(4);
211 :
212 : ResourceOwner resowner;
213 :
214 : ManyTestResourceKind *before_kinds;
215 : ManyTestResourceKind *after_kinds;
216 :
217 : /* Sanity check the arguments */
218 2 : if (nkinds < 0)
219 0 : elog(ERROR, "nkinds must be >= 0");
220 2 : if (nremember_bl < 0)
221 0 : elog(ERROR, "nremember_bl must be >= 0");
222 2 : if (nforget_bl < 0 || nforget_bl > nremember_bl)
223 0 : elog(ERROR, "nforget_bl must between 0 and 'nremember_bl'");
224 2 : if (nremember_al < 0)
225 0 : elog(ERROR, "nremember_al must be greater than zero");
226 2 : if (nforget_al < 0 || nforget_al > nremember_al)
227 0 : elog(ERROR, "nforget_al must between 0 and 'nremember_al'");
228 :
229 : /* Initialize all the different resource kinds to use */
230 2 : before_kinds = palloc(nkinds * sizeof(ManyTestResourceKind));
231 8 : for (int i = 0; i < nkinds; i++)
232 : {
233 6 : InitManyTestResourceKind(&before_kinds[i],
234 : psprintf("resource before locks %d", i),
235 : RESOURCE_RELEASE_BEFORE_LOCKS,
236 6 : RELEASE_PRIO_FIRST + i);
237 : }
238 2 : after_kinds = palloc(nkinds * sizeof(ManyTestResourceKind));
239 8 : for (int i = 0; i < nkinds; i++)
240 : {
241 6 : InitManyTestResourceKind(&after_kinds[i],
242 : psprintf("resource after locks %d", i),
243 : RESOURCE_RELEASE_AFTER_LOCKS,
244 6 : RELEASE_PRIO_FIRST + i);
245 : }
246 :
247 2 : resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
248 :
249 : /* Remember a bunch of resources */
250 2 : if (nremember_bl > 0)
251 : {
252 2 : elog(NOTICE, "remembering %d before-locks resources", nremember_bl);
253 2 : RememberManyTestResources(resowner, before_kinds, nkinds, nremember_bl);
254 : }
255 2 : if (nremember_al > 0)
256 : {
257 2 : elog(NOTICE, "remembering %d after-locks resources", nremember_al);
258 2 : RememberManyTestResources(resowner, after_kinds, nkinds, nremember_al);
259 : }
260 :
261 : /* Forget what was remembered */
262 2 : if (nforget_bl > 0)
263 : {
264 2 : elog(NOTICE, "forgetting %d before-locks resources", nforget_bl);
265 2 : ForgetManyTestResources(resowner, before_kinds, nkinds, nforget_bl);
266 : }
267 :
268 2 : if (nforget_al > 0)
269 : {
270 2 : elog(NOTICE, "forgetting %d after-locks resources", nforget_al);
271 2 : ForgetManyTestResources(resowner, after_kinds, nkinds, nforget_al);
272 : }
273 :
274 : /* Start releasing */
275 2 : elog(NOTICE, "releasing resources before locks");
276 2 : current_release_phase = RESOURCE_RELEASE_BEFORE_LOCKS;
277 2 : last_release_priority = 0;
278 2 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
279 : Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
280 :
281 2 : elog(NOTICE, "releasing locks");
282 2 : current_release_phase = RESOURCE_RELEASE_LOCKS;
283 2 : last_release_priority = 0;
284 2 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, false, false);
285 :
286 2 : elog(NOTICE, "releasing resources after locks");
287 2 : current_release_phase = RESOURCE_RELEASE_AFTER_LOCKS;
288 2 : last_release_priority = 0;
289 2 : ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
290 : Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
291 : Assert(GetTotalResourceCount(after_kinds, nkinds) == 0);
292 :
293 2 : ResourceOwnerDelete(resowner);
294 :
295 2 : PG_RETURN_VOID();
296 : }
|