1/*--------------------------------------------------------------------------
4 * Test ResourceOwner functionality with lots of resources
6 * Copyright (c) 2022-2025, PostgreSQL Global Development Group
9 * src/test/modules/test_resowner/test_resowner_many.c
11 * -------------------------------------------------------------------------
20 * Define a custom resource type to use in the test. The resource being
21 * tracked is a palloc'd ManyTestResource struct.
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
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.
52/* prototypes for local functions */
65/* ResourceOwner callback */
72 Assert(last_release_priority <= mres->kind->desc.release_priority);
80/* ResourceOwner callback */
87 * XXX: we assume that the DebugPrint function is called once for each
88 * leaked resource, and that there are no other callers.
112 * Remember 'nresources' resources. The resources are remembered in round
113 * robin fashion with the kinds from 'kinds' array.
122 for (
int i = 0;
i < nresources;
i++)
126 mres->
kind = &kinds[kind_idx];
136 kind_idx = (kind_idx + 1) % nkinds;
141 * Forget 'nresources' resources, in round robin fashion from 'kinds'.
152 if (ntotal < nresources)
153 elog(
PANIC,
"cannot free %d resources, only %d remembered", nresources, ntotal);
155 for (
int i = 0;
i < nresources;
i++)
159 for (
int j = 0;
j < nkinds;
j++)
161 kind_idx = (kind_idx + 1) % nkinds;
176 elog(
ERROR,
"could not find a test resource to forget");
181 * Get total number of currently active resources among 'kinds'.
188 for (
int i = 0;
i < nkinds;
i++)
189 ntotal += kinds[
i].nremembered - kinds[
i].nforgotten - kinds[
i].nreleased;
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
216 /* Sanity check the arguments */
219 if (nremember_bl < 0)
220 elog(
ERROR,
"nremember_bl must be >= 0");
221 if (nforget_bl < 0 || nforget_bl > nremember_bl)
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");
225 if (nforget_al < 0 || nforget_al > nremember_al)
226 elog(
ERROR,
"nforget_al must between 0 and 'nremember_al'");
228 /* Initialize all the different resource kinds to use */
230 for (
int i = 0;
i < nkinds;
i++)
238 for (
int i = 0;
i < nkinds;
i++)
248 /* Remember a bunch of resources */
249 if (nremember_bl > 0)
251 elog(
NOTICE,
"remembering %d before-locks resources", nremember_bl);
254 if (nremember_al > 0)
256 elog(
NOTICE,
"remembering %d after-locks resources", nremember_al);
260 /* Forget what was remembered */
263 elog(
NOTICE,
"forgetting %d before-locks resources", nforget_bl);
269 elog(
NOTICE,
"forgetting %d after-locks resources", nforget_al);
273 /* Start releasing */
274 elog(
NOTICE,
"releasing resources before locks");
285 elog(
NOTICE,
"releasing resources after locks");
#define PG_GETARG_INT32(n)
Assert(PointerIsAligned(start, uint64))
static void dlist_init(dlist_head *head)
#define dlist_head_element(type, membername, lhead)
static void dlist_delete(dlist_node *node)
static bool dlist_is_empty(const dlist_head *head)
static void dlist_push_tail(dlist_head *head, dlist_node *node)
static void dlist_node_init(dlist_node *node)
void pfree(void *pointer)
static Datum PointerGetDatum(const void *X)
static Pointer DatumGetPointer(Datum X)
char * psprintf(const char *fmt,...)
ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char *name)
ResourceOwner CurrentResourceOwner
void ResourceOwnerRelease(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
void ResourceOwnerDelete(ResourceOwner owner)
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
void ResourceOwnerEnlarge(ResourceOwner owner)
@ RESOURCE_RELEASE_BEFORE_LOCKS
@ RESOURCE_RELEASE_AFTER_LOCKS
#define RELEASE_PRIO_FIRST
dlist_head current_resources
ManyTestResourceKind * kind
char *(* DebugPrint)(Datum res)
ResourceReleasePhase release_phase
void(* ReleaseResource)(Datum res)
ResourceReleasePriority release_priority
static int GetTotalResourceCount(ManyTestResourceKind *kinds, int nkinds)
static void InitManyTestResourceKind(ManyTestResourceKind *kind, char *name, ResourceReleasePhase phase, uint32 priority)
PG_FUNCTION_INFO_V1(test_resowner_many)
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