PostgreSQL Source Code  git master
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-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 {
34  int nreleased;
35  int nleaked;
36 
39 
40 typedef struct
41 {
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  */
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);
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
69 {
71 
72  elog(DEBUG1, "releasing resource %p from %s", mres, mres->kind->desc.name);
73  Assert(last_release_priority <= mres->kind->desc.release_priority);
74 
75  dlist_delete(&mres->node);
76  mres->kind->nreleased++;
78  pfree(mres);
79 }
80 
81 /* ResourceOwner callback */
82 static char *
84 {
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  mres->kind->nleaked++;
92 
93  return psprintf("many-test resource from %s", mres->kind->desc.name);
94 }
95 
96 static void
98  ResourceReleasePhase phase, uint32 priority)
99 {
100  kind->desc.name = name;
101  kind->desc.release_phase = phase;
102  kind->desc.release_priority = priority;
104  kind->desc.DebugPrint = PrintManyTest;
105  kind->nremembered = 0;
106  kind->nforgotten = 0;
107  kind->nreleased = 0;
108  kind->nleaked = 0;
110 }
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
118  ManyTestResourceKind *kinds, int nkinds,
119  int nresources)
120 {
121  int kind_idx = 0;
122 
123  for (int i = 0; i < nresources; i++)
124  {
125  ManyTestResource *mres = palloc(sizeof(ManyTestResource));
126 
127  mres->kind = &kinds[kind_idx];
128  dlist_node_init(&mres->node);
129 
130  ResourceOwnerEnlarge(owner);
131  ResourceOwnerRemember(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
132  kinds[kind_idx].nremembered++;
133  dlist_push_tail(&kinds[kind_idx].current_resources, &mres->node);
134 
135  elog(DEBUG1, "remembered resource %p from %s", mres, mres->kind->desc.name);
136 
137  kind_idx = (kind_idx + 1) % nkinds;
138  }
139 }
140 
141 /*
142  * Forget 'nresources' resources, in round robin fashion from 'kinds'.
143  */
144 static void
146  ManyTestResourceKind *kinds, int nkinds,
147  int nresources)
148 {
149  int kind_idx = 0;
150  int ntotal;
151 
152  ntotal = GetTotalResourceCount(kinds, nkinds);
153  if (ntotal < nresources)
154  elog(PANIC, "cannot free %d resources, only %d remembered", nresources, ntotal);
155 
156  for (int i = 0; i < nresources; i++)
157  {
158  bool found = false;
159 
160  for (int j = 0; j < nkinds; j++)
161  {
162  kind_idx = (kind_idx + 1) % nkinds;
163  if (!dlist_is_empty(&kinds[kind_idx].current_resources))
164  {
165  ManyTestResource *mres = dlist_head_element(ManyTestResource, node, &kinds[kind_idx].current_resources);
166 
167  ResourceOwnerForget(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
168  kinds[kind_idx].nforgotten++;
169  dlist_delete(&mres->node);
170  pfree(mres);
171 
172  found = true;
173  break;
174  }
175  }
176  if (!found)
177  elog(ERROR, "could not find a test resource to forget");
178  }
179 }
180 
181 /*
182  * Get total number of currently active resources among 'kinds'.
183  */
184 static int
186 {
187  int ntotal = 0;
188 
189  for (int i = 0; i < nkinds; i++)
190  ntotal += kinds[i].nremembered - kinds[i].nforgotten - kinds[i].nreleased;
191 
192  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  */
203 Datum
205 {
206  int32 nkinds = PG_GETARG_INT32(0);
207  int32 nremember_bl = PG_GETARG_INT32(1);
208  int32 nforget_bl = PG_GETARG_INT32(2);
209  int32 nremember_al = PG_GETARG_INT32(3);
210  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  if (nkinds < 0)
219  elog(ERROR, "nkinds must be >= 0");
220  if (nremember_bl < 0)
221  elog(ERROR, "nremember_bl must be >= 0");
222  if (nforget_bl < 0 || nforget_bl > nremember_bl)
223  elog(ERROR, "nforget_bl must between 0 and 'nremember_bl'");
224  if (nremember_al < 0)
225  elog(ERROR, "nremember_al must be greater than zero");
226  if (nforget_al < 0 || nforget_al > nremember_al)
227  elog(ERROR, "nforget_al must between 0 and 'nremember_al'");
228 
229  /* Initialize all the different resource kinds to use */
230  before_kinds = palloc(nkinds * sizeof(ManyTestResourceKind));
231  for (int i = 0; i < nkinds; i++)
232  {
233  InitManyTestResourceKind(&before_kinds[i],
234  psprintf("resource before locks %d", i),
237  }
238  after_kinds = palloc(nkinds * sizeof(ManyTestResourceKind));
239  for (int i = 0; i < nkinds; i++)
240  {
241  InitManyTestResourceKind(&after_kinds[i],
242  psprintf("resource after locks %d", i),
245  }
246 
247  resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
248 
249  /* Remember a bunch of resources */
250  if (nremember_bl > 0)
251  {
252  elog(NOTICE, "remembering %d before-locks resources", nremember_bl);
253  RememberManyTestResources(resowner, before_kinds, nkinds, nremember_bl);
254  }
255  if (nremember_al > 0)
256  {
257  elog(NOTICE, "remembering %d after-locks resources", nremember_al);
258  RememberManyTestResources(resowner, after_kinds, nkinds, nremember_al);
259  }
260 
261  /* Forget what was remembered */
262  if (nforget_bl > 0)
263  {
264  elog(NOTICE, "forgetting %d before-locks resources", nforget_bl);
265  ForgetManyTestResources(resowner, before_kinds, nkinds, nforget_bl);
266  }
267 
268  if (nforget_al > 0)
269  {
270  elog(NOTICE, "forgetting %d after-locks resources", nforget_al);
271  ForgetManyTestResources(resowner, after_kinds, nkinds, nforget_al);
272  }
273 
274  /* Start releasing */
275  elog(NOTICE, "releasing resources before locks");
278  ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
279  Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
280 
281  elog(NOTICE, "releasing locks");
284  ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, false, false);
285 
286  elog(NOTICE, "releasing resources after locks");
289  ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
290  Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
291  Assert(GetTotalResourceCount(after_kinds, nkinds) == 0);
292 
293  ResourceOwnerDelete(resowner);
294 
295  PG_RETURN_VOID();
296 }
unsigned int uint32
Definition: c.h:509
signed int int32
Definition: c.h:497
#define Assert(condition)
Definition: c.h:861
#define PANIC
Definition: elog.h:42
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#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:74
int i
Definition: isn.c:73
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc(Size size)
Definition: mcxt.c:1317
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char *name)
Definition: resowner.c:413
ResourceOwner CurrentResourceOwner
Definition: resowner.c:165
void ResourceOwnerRelease(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
Definition: resowner.c:648
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:554
void ResourceOwnerDelete(ResourceOwner owner)
Definition: resowner.c:854
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:514
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition: resowner.c:442
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)
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
const char * name