PostgreSQL Source Code git master
Loading...
Searching...
No Matches
test_resowner_many.c
Go to the documentation of this file.
1/*--------------------------------------------------------------------------
2 *
3 * test_resowner_many.c
4 * Test ResourceOwner functionality with lots of resources
5 *
6 * Copyright (c) 2022-2026, 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/resowner.h"
18
19/*
20 * Define a custom resource type to use in the test. The resource being
21 * tracked is a palloc'd ManyTestResource struct.
22 *
23 * To cross-check that the ResourceOwner calls the callback functions
24 * correctly, we keep track of the remembered resources ourselves in a linked
25 * list, and also keep counters of how many times the callback functions have
26 * been called.
27 */
38
44
45/*
46 * Current release phase, and priority of last call to the release callback.
47 * This is used to check that the resources are released in correct order.
48 */
51
52/* prototypes for local functions */
53static void ReleaseManyTestResource(Datum res);
54static char *PrintManyTest(Datum res);
58 ManyTestResourceKind *kinds, int nkinds,
59 int nresources);
61 ManyTestResourceKind *kinds, int nkinds,
62 int nresources);
64
65/* ResourceOwner callback */
66static void
68{
70
71 elog(DEBUG1, "releasing resource %p from %s", mres, mres->kind->desc.name);
72 Assert(last_release_priority <= mres->kind->desc.release_priority);
73
74 dlist_delete(&mres->node);
75 mres->kind->nreleased++;
76 last_release_priority = mres->kind->desc.release_priority;
77 pfree(mres);
78}
79
80/* ResourceOwner callback */
81static char *
83{
85
86 /*
87 * XXX: we assume that the DebugPrint function is called once for each
88 * leaked resource, and that there are no other callers.
89 */
90 mres->kind->nleaked++;
91
92 return psprintf("many-test resource from %s", mres->kind->desc.name);
93}
94
95static void
98{
99 kind->desc.name = name;
100 kind->desc.release_phase = phase;
104 kind->nremembered = 0;
105 kind->nforgotten = 0;
106 kind->nreleased = 0;
107 kind->nleaked = 0;
109}
110
111/*
112 * Remember 'nresources' resources. The resources are remembered in round
113 * robin fashion with the kinds from 'kinds' array.
114 */
115static void
117 ManyTestResourceKind *kinds, int nkinds,
118 int nresources)
119{
120 int kind_idx = 0;
121
122 for (int i = 0; i < nresources; i++)
123 {
125
126 mres->kind = &kinds[kind_idx];
127 dlist_node_init(&mres->node);
128
130 ResourceOwnerRemember(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
131 kinds[kind_idx].nremembered++;
132 dlist_push_tail(&kinds[kind_idx].current_resources, &mres->node);
133
134 elog(DEBUG1, "remembered resource %p from %s", mres, mres->kind->desc.name);
135
136 kind_idx = (kind_idx + 1) % nkinds;
137 }
138}
139
140/*
141 * Forget 'nresources' resources, in round robin fashion from 'kinds'.
142 */
143static void
145 ManyTestResourceKind *kinds, int nkinds,
146 int nresources)
147{
148 int kind_idx = 0;
149 int ntotal;
150
152 if (ntotal < nresources)
153 elog(PANIC, "cannot free %d resources, only %d remembered", nresources, ntotal);
154
155 for (int i = 0; i < nresources; i++)
156 {
157 bool found = false;
158
159 for (int j = 0; j < nkinds; j++)
160 {
161 kind_idx = (kind_idx + 1) % nkinds;
162 if (!dlist_is_empty(&kinds[kind_idx].current_resources))
163 {
164 ManyTestResource *mres = dlist_head_element(ManyTestResource, node, &kinds[kind_idx].current_resources);
165
166 ResourceOwnerForget(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
167 kinds[kind_idx].nforgotten++;
168 dlist_delete(&mres->node);
169 pfree(mres);
170
171 found = true;
172 break;
173 }
174 }
175 if (!found)
176 elog(ERROR, "could not find a test resource to forget");
177 }
178}
179
180/*
181 * Get total number of currently active resources among 'kinds'.
182 */
183static int
185{
186 int ntotal = 0;
187
188 for (int i = 0; i < nkinds; i++)
189 ntotal += kinds[i].nremembered - kinds[i].nforgotten - kinds[i].nreleased;
190
191 return ntotal;
192}
193
194/*
195 * Remember lots of resources, belonging to 'nkinds' different resource types
196 * with different priorities. Then forget some of them, and finally, release
197 * the resource owner. We use a custom resource type that performs various
198 * sanity checks to verify that all the resources are released, and in the
199 * correct order.
200 */
202Datum
204{
210
211 ResourceOwner resowner;
212
215
216 /* Sanity check the arguments */
217 if (nkinds < 0)
218 elog(ERROR, "nkinds must be >= 0");
219 if (nremember_bl < 0)
220 elog(ERROR, "nremember_bl must be >= 0");
222 elog(ERROR, "nforget_bl must between 0 and 'nremember_bl'");
223 if (nremember_al < 0)
224 elog(ERROR, "nremember_al must be greater than zero");
226 elog(ERROR, "nforget_al must between 0 and 'nremember_al'");
227
228 /* Initialize all the different resource kinds to use */
230 for (int i = 0; i < nkinds; i++)
231 {
233 psprintf("resource before locks %d", i),
236 }
238 for (int i = 0; i < nkinds; i++)
239 {
241 psprintf("resource after locks %d", i),
244 }
245
246 resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
247
248 /* Remember a bunch of resources */
249 if (nremember_bl > 0)
250 {
251 elog(NOTICE, "remembering %d before-locks resources", nremember_bl);
253 }
254 if (nremember_al > 0)
255 {
256 elog(NOTICE, "remembering %d after-locks resources", nremember_al);
258 }
259
260 /* Forget what was remembered */
261 if (nforget_bl > 0)
262 {
263 elog(NOTICE, "forgetting %d before-locks resources", nforget_bl);
265 }
266
267 if (nforget_al > 0)
268 {
269 elog(NOTICE, "forgetting %d after-locks resources", nforget_al);
271 }
272
273 /* Start releasing */
274 elog(NOTICE, "releasing resources before locks");
279
280 elog(NOTICE, "releasing locks");
283 ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, false, false);
284
285 elog(NOTICE, "releasing resources after locks");
288 ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
291
292 ResourceOwnerDelete(resowner);
293
295}
#define Assert(condition)
Definition c.h:873
int32_t int32
Definition c.h:542
uint32_t uint32
Definition c.h:546
#define PANIC
Definition elog.h:42
#define DEBUG1
Definition elog.h:30
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define NOTICE
Definition elog.h:35
#define palloc_object(type)
Definition fe_memutils.h:74
#define PG_RETURN_VOID()
Definition fmgr.h:350
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_GETARG_INT32(n)
Definition fmgr.h:269
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
static void dlist_init(dlist_head *head)
Definition ilist.h:314
#define dlist_head_element(type, membername, lhead)
Definition ilist.h:603
static void dlist_delete(dlist_node *node)
Definition ilist.h:405
static bool dlist_is_empty(const dlist_head *head)
Definition ilist.h:336
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition ilist.h:364
static void dlist_node_init(dlist_node *node)
Definition ilist.h:325
int j
Definition isn.c:78
int i
Definition isn.c:77
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc(Size size)
Definition mcxt.c:1387
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char *name)
Definition resowner.c:418
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
void ResourceOwnerRelease(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
Definition resowner.c:655
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition resowner.c:561
void ResourceOwnerDelete(ResourceOwner owner)
Definition resowner.c:868
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition resowner.c:521
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition resowner.c:449
ResourceReleasePhase
Definition resowner.h:53
@ RESOURCE_RELEASE_LOCKS
Definition resowner.h:55
@ RESOURCE_RELEASE_BEFORE_LOCKS
Definition resowner.h:54
@ RESOURCE_RELEASE_AFTER_LOCKS
Definition resowner.h:56
#define RELEASE_PRIO_FIRST
Definition resowner.h:80
ResourceOwnerDesc desc
ManyTestResourceKind * kind
char *(* DebugPrint)(Datum res)
Definition resowner.h:118
ResourceReleasePhase release_phase
Definition resowner.h:96
void(* ReleaseResource)(Datum res)
Definition resowner.h:108
ResourceReleasePriority release_priority
Definition resowner.h:97
const char * name
Definition resowner.h:93
static int GetTotalResourceCount(ManyTestResourceKind *kinds, int nkinds)
static void InitManyTestResourceKind(ManyTestResourceKind *kind, char *name, ResourceReleasePhase phase, uint32 priority)
static void ForgetManyTestResources(ResourceOwner owner, ManyTestResourceKind *kinds, int nkinds, int nresources)
static void ReleaseManyTestResource(Datum res)
static ResourceReleasePhase current_release_phase
static void RememberManyTestResources(ResourceOwner owner, ManyTestResourceKind *kinds, int nkinds, int nresources)
Datum test_resowner_many(PG_FUNCTION_ARGS)
static char * PrintManyTest(Datum res)
static uint32 last_release_priority
const char * name