Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * resowner.c
4 : * POSTGRES resource owner management code.
5 : *
6 : * Query-lifespan resources are tracked by associating them with
7 : * ResourceOwner objects. This provides a simple mechanism for ensuring
8 : * that such resources are freed at the right time.
9 : * See utils/resowner/README for more info.
10 : *
11 : *
12 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
13 : * Portions Copyright (c) 1994, Regents of the University of California
14 : *
15 : *
16 : * IDENTIFICATION
17 : * src/backend/utils/resowner/resowner.c
18 : *
19 : *-------------------------------------------------------------------------
20 : */
21 : #include "postgres.h"
22 :
23 : #include "common/cryptohash.h"
24 : #include "common/hashfn.h"
25 : #include "common/hmac.h"
26 : #include "jit/jit.h"
27 : #include "storage/bufmgr.h"
28 : #include "storage/ipc.h"
29 : #include "storage/predicate.h"
30 : #include "storage/proc.h"
31 : #include "utils/memutils.h"
32 : #include "utils/rel.h"
33 : #include "utils/resowner_private.h"
34 : #include "utils/snapmgr.h"
35 :
36 :
37 : /*
38 : * All resource IDs managed by this code are required to fit into a Datum,
39 : * which is fine since they are generally pointers or integers.
40 : *
41 : * Provide Datum conversion macros for a couple of things that are really
42 : * just "int".
43 : */
44 : #define FileGetDatum(file) Int32GetDatum(file)
45 : #define DatumGetFile(datum) ((File) DatumGetInt32(datum))
46 : #define BufferGetDatum(buffer) Int32GetDatum(buffer)
47 : #define DatumGetBuffer(datum) ((Buffer) DatumGetInt32(datum))
48 :
49 : /*
50 : * ResourceArray is a common structure for storing all types of resource IDs.
51 : *
52 : * We manage small sets of resource IDs by keeping them in a simple array:
53 : * itemsarr[k] holds an ID, for 0 <= k < nitems <= maxitems = capacity.
54 : *
55 : * If a set grows large, we switch over to using open-addressing hashing.
56 : * Then, itemsarr[] is a hash table of "capacity" slots, with each
57 : * slot holding either an ID or "invalidval". nitems is the number of valid
58 : * items present; if it would exceed maxitems, we enlarge the array and
59 : * re-hash. In this mode, maxitems should be rather less than capacity so
60 : * that we don't waste too much time searching for empty slots.
61 : *
62 : * In either mode, lastidx remembers the location of the last item inserted
63 : * or returned by GetAny; this speeds up searches in ResourceArrayRemove.
64 : */
65 : typedef struct ResourceArray
66 : {
67 : Datum *itemsarr; /* buffer for storing values */
68 : Datum invalidval; /* value that is considered invalid */
69 : uint32 capacity; /* allocated length of itemsarr[] */
70 : uint32 nitems; /* how many items are stored in items array */
71 : uint32 maxitems; /* current limit on nitems before enlarging */
72 : uint32 lastidx; /* index of last item returned by GetAny */
73 : } ResourceArray;
74 :
75 : /*
76 : * Initially allocated size of a ResourceArray. Must be power of two since
77 : * we'll use (arraysize - 1) as mask for hashing.
78 : */
79 : #define RESARRAY_INIT_SIZE 16
80 :
81 : /*
82 : * When to switch to hashing vs. simple array logic in a ResourceArray.
83 : */
84 : #define RESARRAY_MAX_ARRAY 64
85 : #define RESARRAY_IS_ARRAY(resarr) ((resarr)->capacity <= RESARRAY_MAX_ARRAY)
86 :
87 : /*
88 : * How many items may be stored in a resource array of given capacity.
89 : * When this number is reached, we must resize.
90 : */
91 : #define RESARRAY_MAX_ITEMS(capacity) \
92 : ((capacity) <= RESARRAY_MAX_ARRAY ? (capacity) : (capacity)/4 * 3)
93 :
94 : /*
95 : * To speed up bulk releasing or reassigning locks from a resource owner to
96 : * its parent, each resource owner has a small cache of locks it owns. The
97 : * lock manager has the same information in its local lock hash table, and
98 : * we fall back on that if cache overflows, but traversing the hash table
99 : * is slower when there are a lot of locks belonging to other resource owners.
100 : *
101 : * MAX_RESOWNER_LOCKS is the size of the per-resource owner cache. It's
102 : * chosen based on some testing with pg_dump with a large schema. When the
103 : * tests were done (on 9.2), resource owners in a pg_dump run contained up
104 : * to 9 locks, regardless of the schema size, except for the top resource
105 : * owner which contained much more (overflowing the cache). 15 seems like a
106 : * nice round number that's somewhat higher than what pg_dump needs. Note that
107 : * making this number larger is not free - the bigger the cache, the slower
108 : * it is to release locks (in retail), when a resource owner holds many locks.
109 : */
110 : #define MAX_RESOWNER_LOCKS 15
111 :
112 : /*
113 : * ResourceOwner objects look like this
114 : */
115 : typedef struct ResourceOwnerData
116 : {
117 : ResourceOwner parent; /* NULL if no parent (toplevel owner) */
118 : ResourceOwner firstchild; /* head of linked list of children */
119 : ResourceOwner nextchild; /* next child of same parent */
120 : const char *name; /* name (just for debugging) */
121 :
122 : /* We have built-in support for remembering: */
123 : ResourceArray bufferarr; /* owned buffers */
124 : ResourceArray bufferioarr; /* in-progress buffer IO */
125 : ResourceArray catrefarr; /* catcache references */
126 : ResourceArray catlistrefarr; /* catcache-list pins */
127 : ResourceArray relrefarr; /* relcache references */
128 : ResourceArray planrefarr; /* plancache references */
129 : ResourceArray tupdescarr; /* tupdesc references */
130 : ResourceArray snapshotarr; /* snapshot references */
131 : ResourceArray filearr; /* open temporary files */
132 : ResourceArray dsmarr; /* dynamic shmem segments */
133 : ResourceArray jitarr; /* JIT contexts */
134 : ResourceArray cryptohasharr; /* cryptohash contexts */
135 : ResourceArray hmacarr; /* HMAC contexts */
136 :
137 : /* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
138 : int nlocks; /* number of owned locks */
139 : LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */
140 : } ResourceOwnerData;
141 :
142 :
143 : /*****************************************************************************
144 : * GLOBAL MEMORY *
145 : *****************************************************************************/
146 :
147 : ResourceOwner CurrentResourceOwner = NULL;
148 : ResourceOwner CurTransactionResourceOwner = NULL;
149 : ResourceOwner TopTransactionResourceOwner = NULL;
150 : ResourceOwner AuxProcessResourceOwner = NULL;
151 :
152 : /*
153 : * List of add-on callbacks for resource releasing
154 : */
155 : typedef struct ResourceReleaseCallbackItem
156 : {
157 : struct ResourceReleaseCallbackItem *next;
158 : ResourceReleaseCallback callback;
159 : void *arg;
160 : } ResourceReleaseCallbackItem;
161 :
162 : static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
163 :
164 :
165 : /* Internal routines */
166 : static void ResourceArrayInit(ResourceArray *resarr, Datum invalidval);
167 : static void ResourceArrayEnlarge(ResourceArray *resarr);
168 : static void ResourceArrayAdd(ResourceArray *resarr, Datum value);
169 : static bool ResourceArrayRemove(ResourceArray *resarr, Datum value);
170 : static bool ResourceArrayGetAny(ResourceArray *resarr, Datum *value);
171 : static void ResourceArrayFree(ResourceArray *resarr);
172 : static void ResourceOwnerReleaseInternal(ResourceOwner owner,
173 : ResourceReleasePhase phase,
174 : bool isCommit,
175 : bool isTopLevel);
176 : static void ReleaseAuxProcessResourcesCallback(int code, Datum arg);
177 : static void PrintRelCacheLeakWarning(Relation rel);
178 : static void PrintPlanCacheLeakWarning(CachedPlan *plan);
179 : static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
180 : static void PrintSnapshotLeakWarning(Snapshot snapshot);
181 : static void PrintFileLeakWarning(File file);
182 : static void PrintDSMLeakWarning(dsm_segment *seg);
183 : static void PrintCryptoHashLeakWarning(Datum handle);
184 : static void PrintHMACLeakWarning(Datum handle);
185 :
186 :
187 : /*****************************************************************************
188 : * INTERNAL ROUTINES *
189 : *****************************************************************************/
190 :
191 :
192 : /*
193 : * Initialize a ResourceArray
194 : */
195 : static void
196 14581424 : ResourceArrayInit(ResourceArray *resarr, Datum invalidval)
197 : {
198 : /* Assert it's empty */
199 : Assert(resarr->itemsarr == NULL);
200 : Assert(resarr->capacity == 0);
201 : Assert(resarr->nitems == 0);
202 : Assert(resarr->maxitems == 0);
203 : /* Remember the appropriate "invalid" value */
204 14581424 : resarr->invalidval = invalidval;
205 : /* We don't allocate any storage until needed */
206 14581424 : }
207 :
208 : /*
209 : * Make sure there is room for at least one more resource in an array.
210 : *
211 : * This is separate from actually inserting a resource because if we run out
212 : * of memory, it's critical to do so *before* acquiring the resource.
213 : */
214 : static void
215 255338986 : ResourceArrayEnlarge(ResourceArray *resarr)
216 : {
217 : uint32 i,
218 : oldcap,
219 : newcap;
220 : Datum *olditemsarr;
221 : Datum *newitemsarr;
222 :
223 255338986 : if (resarr->nitems < resarr->maxitems)
224 251427116 : return; /* no work needed */
225 :
226 3911870 : olditemsarr = resarr->itemsarr;
227 3911870 : oldcap = resarr->capacity;
228 :
229 : /* Double the capacity of the array (capacity must stay a power of 2!) */
230 3911870 : newcap = (oldcap > 0) ? oldcap * 2 : RESARRAY_INIT_SIZE;
231 3911870 : newitemsarr = (Datum *) MemoryContextAlloc(TopMemoryContext,
232 : newcap * sizeof(Datum));
233 68453438 : for (i = 0; i < newcap; i++)
234 64541568 : newitemsarr[i] = resarr->invalidval;
235 :
236 : /* We assume we can't fail below this point, so OK to scribble on resarr */
237 3911870 : resarr->itemsarr = newitemsarr;
238 3911870 : resarr->capacity = newcap;
239 3911870 : resarr->maxitems = RESARRAY_MAX_ITEMS(newcap);
240 3911870 : resarr->nitems = 0;
241 :
242 3911870 : if (olditemsarr != NULL)
243 : {
244 : /*
245 : * Transfer any pre-existing entries into the new array; they don't
246 : * necessarily go where they were before, so this simple logic is the
247 : * best way. Note that if we were managing the set as a simple array,
248 : * the entries after nitems are garbage, but that shouldn't matter
249 : * because we won't get here unless nitems was equal to oldcap.
250 : */
251 1068074 : for (i = 0; i < oldcap; i++)
252 : {
253 1057824 : if (olditemsarr[i] != resarr->invalidval)
254 841568 : ResourceArrayAdd(resarr, olditemsarr[i]);
255 : }
256 :
257 : /* And release old array. */
258 10250 : pfree(olditemsarr);
259 : }
260 :
261 : Assert(resarr->nitems < resarr->maxitems);
262 : }
263 :
264 : /*
265 : * Add a resource to ResourceArray
266 : *
267 : * Caller must have previously done ResourceArrayEnlarge()
268 : */
269 : static void
270 253493770 : ResourceArrayAdd(ResourceArray *resarr, Datum value)
271 : {
272 : uint32 idx;
273 :
274 : Assert(value != resarr->invalidval);
275 : Assert(resarr->nitems < resarr->maxitems);
276 :
277 253493770 : if (RESARRAY_IS_ARRAY(resarr))
278 : {
279 : /* Append to linear array. */
280 251068124 : idx = resarr->nitems;
281 : }
282 : else
283 : {
284 : /* Insert into first free slot at or after hash location. */
285 2425646 : uint32 mask = resarr->capacity - 1;
286 :
287 2425646 : idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask;
288 : for (;;)
289 : {
290 569323976 : if (resarr->itemsarr[idx] == resarr->invalidval)
291 2425646 : break;
292 566898330 : idx = (idx + 1) & mask;
293 : }
294 : }
295 253493770 : resarr->lastidx = idx;
296 253493770 : resarr->itemsarr[idx] = value;
297 253493770 : resarr->nitems++;
298 253493770 : }
299 :
300 : /*
301 : * Remove a resource from ResourceArray
302 : *
303 : * Returns true on success, false if resource was not found.
304 : *
305 : * Note: if same resource ID appears more than once, one instance is removed.
306 : */
307 : static bool
308 252652192 : ResourceArrayRemove(ResourceArray *resarr, Datum value)
309 : {
310 : uint32 i,
311 : idx,
312 252652192 : lastidx = resarr->lastidx;
313 :
314 : Assert(value != resarr->invalidval);
315 :
316 : /* Search through all items, but try lastidx first. */
317 252652192 : if (RESARRAY_IS_ARRAY(resarr))
318 : {
319 250875314 : if (lastidx < resarr->nitems &&
320 250875314 : resarr->itemsarr[lastidx] == value)
321 : {
322 244036548 : resarr->itemsarr[lastidx] = resarr->itemsarr[resarr->nitems - 1];
323 244036548 : resarr->nitems--;
324 : /* Update lastidx to make reverse-order removals fast. */
325 244036548 : resarr->lastidx = resarr->nitems - 1;
326 244036548 : return true;
327 : }
328 14005730 : for (i = 0; i < resarr->nitems; i++)
329 : {
330 14005730 : if (resarr->itemsarr[i] == value)
331 : {
332 6838766 : resarr->itemsarr[i] = resarr->itemsarr[resarr->nitems - 1];
333 6838766 : resarr->nitems--;
334 : /* Update lastidx to make reverse-order removals fast. */
335 6838766 : resarr->lastidx = resarr->nitems - 1;
336 6838766 : return true;
337 : }
338 : }
339 : }
340 : else
341 : {
342 1776878 : uint32 mask = resarr->capacity - 1;
343 :
344 1776878 : if (lastidx < resarr->capacity &&
345 1776878 : resarr->itemsarr[lastidx] == value)
346 : {
347 1242422 : resarr->itemsarr[lastidx] = resarr->invalidval;
348 1242422 : resarr->nitems--;
349 1242422 : return true;
350 : }
351 534456 : idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask;
352 185268444 : for (i = 0; i < resarr->capacity; i++)
353 : {
354 185268444 : if (resarr->itemsarr[idx] == value)
355 : {
356 534456 : resarr->itemsarr[idx] = resarr->invalidval;
357 534456 : resarr->nitems--;
358 534456 : return true;
359 : }
360 184733988 : idx = (idx + 1) & mask;
361 : }
362 : }
363 :
364 0 : return false;
365 : }
366 :
367 : /*
368 : * Get any convenient entry in a ResourceArray.
369 : *
370 : * "Convenient" is defined as "easy for ResourceArrayRemove to remove";
371 : * we help that along by setting lastidx to match. This avoids O(N^2) cost
372 : * when removing all ResourceArray items during ResourceOwner destruction.
373 : *
374 : * Returns true if we found an element, or false if the array is empty.
375 : */
376 : static bool
377 14771572 : ResourceArrayGetAny(ResourceArray *resarr, Datum *value)
378 : {
379 14771572 : if (resarr->nitems == 0)
380 14585864 : return false;
381 :
382 185708 : if (RESARRAY_IS_ARRAY(resarr))
383 : {
384 : /* Linear array: just return the first element. */
385 167742 : resarr->lastidx = 0;
386 : }
387 : else
388 : {
389 : /* Hash: search forward from wherever we were last. */
390 17966 : uint32 mask = resarr->capacity - 1;
391 :
392 : for (;;)
393 : {
394 44294 : resarr->lastidx &= mask;
395 44294 : if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
396 17966 : break;
397 26328 : resarr->lastidx++;
398 : }
399 : }
400 :
401 185708 : *value = resarr->itemsarr[resarr->lastidx];
402 185708 : return true;
403 : }
404 :
405 : /*
406 : * Trash a ResourceArray (we don't care about its state after this)
407 : */
408 : static void
409 14533272 : ResourceArrayFree(ResourceArray *resarr)
410 : {
411 14533272 : if (resarr->itemsarr)
412 3897174 : pfree(resarr->itemsarr);
413 14533272 : }
414 :
415 :
416 : /*****************************************************************************
417 : * EXPORTED ROUTINES *
418 : *****************************************************************************/
419 :
420 :
421 : /*
422 : * ResourceOwnerCreate
423 : * Create an empty ResourceOwner.
424 : *
425 : * All ResourceOwner objects are kept in TopMemoryContext, since they should
426 : * only be freed explicitly.
427 : */
428 : ResourceOwner
429 1121648 : ResourceOwnerCreate(ResourceOwner parent, const char *name)
430 : {
431 : ResourceOwner owner;
432 :
433 1121648 : owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
434 : sizeof(ResourceOwnerData));
435 1121648 : owner->name = name;
436 :
437 1121648 : if (parent)
438 : {
439 616820 : owner->parent = parent;
440 616820 : owner->nextchild = parent->firstchild;
441 616820 : parent->firstchild = owner;
442 : }
443 :
444 1121648 : ResourceArrayInit(&(owner->bufferarr), BufferGetDatum(InvalidBuffer));
445 1121648 : ResourceArrayInit(&(owner->bufferioarr), BufferGetDatum(InvalidBuffer));
446 1121648 : ResourceArrayInit(&(owner->catrefarr), PointerGetDatum(NULL));
447 1121648 : ResourceArrayInit(&(owner->catlistrefarr), PointerGetDatum(NULL));
448 1121648 : ResourceArrayInit(&(owner->relrefarr), PointerGetDatum(NULL));
449 1121648 : ResourceArrayInit(&(owner->planrefarr), PointerGetDatum(NULL));
450 1121648 : ResourceArrayInit(&(owner->tupdescarr), PointerGetDatum(NULL));
451 1121648 : ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
452 1121648 : ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
453 1121648 : ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
454 1121648 : ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
455 1121648 : ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL));
456 1121648 : ResourceArrayInit(&(owner->hmacarr), PointerGetDatum(NULL));
457 :
458 1121648 : return owner;
459 : }
460 :
461 : /*
462 : * ResourceOwnerRelease
463 : * Release all resources owned by a ResourceOwner and its descendants,
464 : * but don't delete the owner objects themselves.
465 : *
466 : * Note that this executes just one phase of release, and so typically
467 : * must be called three times. We do it this way because (a) we want to
468 : * do all the recursion separately for each phase, thereby preserving
469 : * the needed order of operations; and (b) xact.c may have other operations
470 : * to do between the phases.
471 : *
472 : * phase: release phase to execute
473 : * isCommit: true for successful completion of a query or transaction,
474 : * false for unsuccessful
475 : * isTopLevel: true if completing a main transaction, else false
476 : *
477 : * isCommit is passed because some modules may expect that their resources
478 : * were all released already if the transaction or portal finished normally.
479 : * If so it is reasonable to give a warning (NOT an error) should any
480 : * unreleased resources be present. When isCommit is false, such warnings
481 : * are generally inappropriate.
482 : *
483 : * isTopLevel is passed when we are releasing TopTransactionResourceOwner
484 : * at completion of a main transaction. This generally means that *all*
485 : * resources will be released, and so we can optimize things a bit.
486 : */
487 : void
488 3200412 : ResourceOwnerRelease(ResourceOwner owner,
489 : ResourceReleasePhase phase,
490 : bool isCommit,
491 : bool isTopLevel)
492 : {
493 : /* There's not currently any setup needed before recursing */
494 3200412 : ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
495 3200412 : }
496 :
497 : static void
498 3362604 : ResourceOwnerReleaseInternal(ResourceOwner owner,
499 : ResourceReleasePhase phase,
500 : bool isCommit,
501 : bool isTopLevel)
502 : {
503 : ResourceOwner child;
504 : ResourceOwner save;
505 : ResourceReleaseCallbackItem *item;
506 : ResourceReleaseCallbackItem *next;
507 : Datum foundres;
508 :
509 : /* Recurse to handle descendants */
510 3524796 : for (child = owner->firstchild; child != NULL; child = child->nextchild)
511 162192 : ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
512 :
513 : /*
514 : * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
515 : * get confused.
516 : */
517 3362604 : save = CurrentResourceOwner;
518 3362604 : CurrentResourceOwner = owner;
519 :
520 3362604 : if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
521 : {
522 : /*
523 : * Abort failed buffer IO. AbortBufferIO()->TerminateBufferIO() calls
524 : * ResourceOwnerForgetBufferIO(), so we just have to iterate till
525 : * there are none.
526 : *
527 : * Needs to be before we release buffer pins.
528 : *
529 : * During a commit, there shouldn't be any in-progress IO.
530 : */
531 1120894 : while (ResourceArrayGetAny(&(owner->bufferioarr), &foundres))
532 : {
533 26 : Buffer res = DatumGetBuffer(foundres);
534 :
535 26 : if (isCommit)
536 0 : elog(PANIC, "lost track of buffer IO on buffer %d", res);
537 26 : AbortBufferIO(res);
538 : }
539 :
540 : /*
541 : * Release buffer pins. Note that ReleaseBuffer will remove the
542 : * buffer entry from our array, so we just have to iterate till there
543 : * are none.
544 : *
545 : * During a commit, there shouldn't be any remaining pins --- that
546 : * would indicate failure to clean up the executor correctly --- so
547 : * issue warnings. In the abort case, just clean up quietly.
548 : */
549 1128202 : while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
550 : {
551 7334 : Buffer res = DatumGetBuffer(foundres);
552 :
553 7334 : if (isCommit)
554 0 : PrintBufferLeakWarning(res);
555 7334 : ReleaseBuffer(res);
556 : }
557 :
558 : /* Ditto for relcache references */
559 1152782 : while (ResourceArrayGetAny(&(owner->relrefarr), &foundres))
560 : {
561 31914 : Relation res = (Relation) DatumGetPointer(foundres);
562 :
563 31914 : if (isCommit)
564 0 : PrintRelCacheLeakWarning(res);
565 31914 : RelationClose(res);
566 : }
567 :
568 : /* Ditto for dynamic shared memory segments */
569 1120868 : while (ResourceArrayGetAny(&(owner->dsmarr), &foundres))
570 : {
571 0 : dsm_segment *res = (dsm_segment *) DatumGetPointer(foundres);
572 :
573 0 : if (isCommit)
574 0 : PrintDSMLeakWarning(res);
575 0 : dsm_detach(res);
576 : }
577 :
578 : /* Ditto for JIT contexts */
579 1120884 : while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
580 : {
581 16 : JitContext *context = (JitContext *) DatumGetPointer(foundres);
582 :
583 16 : jit_release_context(context);
584 : }
585 :
586 : /* Ditto for cryptohash contexts */
587 1120876 : while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
588 : {
589 : pg_cryptohash_ctx *context =
590 8 : (pg_cryptohash_ctx *) DatumGetPointer(foundres);
591 :
592 8 : if (isCommit)
593 0 : PrintCryptoHashLeakWarning(foundres);
594 8 : pg_cryptohash_free(context);
595 : }
596 :
597 : /* Ditto for HMAC contexts */
598 1120868 : while (ResourceArrayGetAny(&(owner->hmacarr), &foundres))
599 : {
600 0 : pg_hmac_ctx *context = (pg_hmac_ctx *) DatumGetPointer(foundres);
601 :
602 0 : if (isCommit)
603 0 : PrintHMACLeakWarning(foundres);
604 0 : pg_hmac_free(context);
605 : }
606 : }
607 2241736 : else if (phase == RESOURCE_RELEASE_LOCKS)
608 : {
609 1120868 : if (isTopLevel)
610 : {
611 : /*
612 : * For a top-level xact we are going to release all locks (or at
613 : * least all non-session locks), so just do a single lmgr call at
614 : * the top of the recursion.
615 : */
616 557864 : if (owner == TopTransactionResourceOwner)
617 : {
618 499990 : ProcReleaseLocks(isCommit);
619 499990 : ReleasePredicateLocks(isCommit, false);
620 : }
621 : }
622 : else
623 : {
624 : /*
625 : * Release locks retail. Note that if we are committing a
626 : * subtransaction, we do NOT release its locks yet, but transfer
627 : * them to the parent.
628 : */
629 : LOCALLOCK **locks;
630 : int nlocks;
631 :
632 : Assert(owner->parent != NULL);
633 :
634 : /*
635 : * Pass the list of locks owned by this resource owner to the lock
636 : * manager, unless it has overflowed.
637 : */
638 563004 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
639 : {
640 5436 : locks = NULL;
641 5436 : nlocks = 0;
642 : }
643 : else
644 : {
645 557568 : locks = owner->locks;
646 557568 : nlocks = owner->nlocks;
647 : }
648 :
649 563004 : if (isCommit)
650 553754 : LockReassignCurrentOwner(locks, nlocks);
651 : else
652 9250 : LockReleaseCurrentOwner(locks, nlocks);
653 : }
654 : }
655 1120868 : else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
656 : {
657 : /*
658 : * Release catcache references. Note that ReleaseCatCache will remove
659 : * the catref entry from our array, so we just have to iterate till
660 : * there are none.
661 : *
662 : * As with buffer pins, warn if any are left at commit time.
663 : */
664 1130116 : while (ResourceArrayGetAny(&(owner->catrefarr), &foundres))
665 : {
666 9248 : HeapTuple res = (HeapTuple) DatumGetPointer(foundres);
667 :
668 9248 : if (isCommit)
669 0 : PrintCatCacheLeakWarning(res);
670 9248 : ReleaseCatCache(res);
671 : }
672 :
673 : /* Ditto for catcache lists */
674 1120904 : while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres))
675 : {
676 36 : CatCList *res = (CatCList *) DatumGetPointer(foundres);
677 :
678 36 : if (isCommit)
679 0 : PrintCatCacheListLeakWarning(res);
680 36 : ReleaseCatCacheList(res);
681 : }
682 :
683 : /* Ditto for plancache references */
684 1130988 : while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
685 : {
686 10120 : CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);
687 :
688 10120 : if (isCommit)
689 0 : PrintPlanCacheLeakWarning(res);
690 10120 : ReleaseCachedPlan(res, owner);
691 : }
692 :
693 : /* Ditto for tupdesc references */
694 1131936 : while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres))
695 : {
696 11068 : TupleDesc res = (TupleDesc) DatumGetPointer(foundres);
697 :
698 11068 : if (isCommit)
699 0 : PrintTupleDescLeakWarning(res);
700 11068 : DecrTupleDescRefCount(res);
701 : }
702 :
703 : /* Ditto for snapshot references */
704 1171176 : while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres))
705 : {
706 50308 : Snapshot res = (Snapshot) DatumGetPointer(foundres);
707 :
708 50308 : if (isCommit)
709 0 : PrintSnapshotLeakWarning(res);
710 50308 : UnregisterSnapshot(res);
711 : }
712 :
713 : /* Ditto for temporary files */
714 1120876 : while (ResourceArrayGetAny(&(owner->filearr), &foundres))
715 : {
716 8 : File res = DatumGetFile(foundres);
717 :
718 8 : if (isCommit)
719 0 : PrintFileLeakWarning(res);
720 8 : FileClose(res);
721 : }
722 : }
723 :
724 : /* Let add-on modules get a chance too */
725 3366120 : for (item = ResourceRelease_callbacks; item; item = next)
726 : {
727 : /* allow callbacks to unregister themselves when called */
728 3516 : next = item->next;
729 3516 : item->callback(phase, isCommit, isTopLevel, item->arg);
730 : }
731 :
732 3362604 : CurrentResourceOwner = save;
733 3362604 : }
734 :
735 : /*
736 : * ResourceOwnerReleaseAllPlanCacheRefs
737 : * Release the plancache references (only) held by this owner.
738 : *
739 : * We might eventually add similar functions for other resource types,
740 : * but for now, only this is needed.
741 : */
742 : void
743 14580 : ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner)
744 : {
745 : Datum foundres;
746 :
747 80202 : while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
748 : {
749 65622 : CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);
750 :
751 65622 : ReleaseCachedPlan(res, owner);
752 : }
753 14580 : }
754 :
755 : /*
756 : * ResourceOwnerDelete
757 : * Delete an owner object and its descendants.
758 : *
759 : * The caller must have already released all resources in the object tree.
760 : */
761 : void
762 1117944 : ResourceOwnerDelete(ResourceOwner owner)
763 : {
764 : /* We had better not be deleting CurrentResourceOwner ... */
765 : Assert(owner != CurrentResourceOwner);
766 :
767 : /* And it better not own any resources, either */
768 : Assert(owner->bufferarr.nitems == 0);
769 : Assert(owner->bufferioarr.nitems == 0);
770 : Assert(owner->catrefarr.nitems == 0);
771 : Assert(owner->catlistrefarr.nitems == 0);
772 : Assert(owner->relrefarr.nitems == 0);
773 : Assert(owner->planrefarr.nitems == 0);
774 : Assert(owner->tupdescarr.nitems == 0);
775 : Assert(owner->snapshotarr.nitems == 0);
776 : Assert(owner->filearr.nitems == 0);
777 : Assert(owner->dsmarr.nitems == 0);
778 : Assert(owner->jitarr.nitems == 0);
779 : Assert(owner->cryptohasharr.nitems == 0);
780 : Assert(owner->hmacarr.nitems == 0);
781 : Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
782 :
783 : /*
784 : * Delete children. The recursive call will delink the child from me, so
785 : * just iterate as long as there is a child.
786 : */
787 1172014 : while (owner->firstchild != NULL)
788 54070 : ResourceOwnerDelete(owner->firstchild);
789 :
790 : /*
791 : * We delink the owner from its parent before deleting it, so that if
792 : * there's an error we won't have deleted/busted owners still attached to
793 : * the owner tree. Better a leak than a crash.
794 : */
795 1117944 : ResourceOwnerNewParent(owner, NULL);
796 :
797 : /* And free the object. */
798 1117944 : ResourceArrayFree(&(owner->bufferarr));
799 1117944 : ResourceArrayFree(&(owner->bufferioarr));
800 1117944 : ResourceArrayFree(&(owner->catrefarr));
801 1117944 : ResourceArrayFree(&(owner->catlistrefarr));
802 1117944 : ResourceArrayFree(&(owner->relrefarr));
803 1117944 : ResourceArrayFree(&(owner->planrefarr));
804 1117944 : ResourceArrayFree(&(owner->tupdescarr));
805 1117944 : ResourceArrayFree(&(owner->snapshotarr));
806 1117944 : ResourceArrayFree(&(owner->filearr));
807 1117944 : ResourceArrayFree(&(owner->dsmarr));
808 1117944 : ResourceArrayFree(&(owner->jitarr));
809 1117944 : ResourceArrayFree(&(owner->cryptohasharr));
810 1117944 : ResourceArrayFree(&(owner->hmacarr));
811 :
812 1117944 : pfree(owner);
813 1117944 : }
814 :
815 : /*
816 : * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
817 : */
818 : ResourceOwner
819 553754 : ResourceOwnerGetParent(ResourceOwner owner)
820 : {
821 553754 : return owner->parent;
822 : }
823 :
824 : /*
825 : * Reassign a ResourceOwner to have a new parent
826 : */
827 : void
828 1118018 : ResourceOwnerNewParent(ResourceOwner owner,
829 : ResourceOwner newparent)
830 : {
831 1118018 : ResourceOwner oldparent = owner->parent;
832 :
833 1118018 : if (oldparent)
834 : {
835 616894 : if (owner == oldparent->firstchild)
836 602272 : oldparent->firstchild = owner->nextchild;
837 : else
838 : {
839 : ResourceOwner child;
840 :
841 16460 : for (child = oldparent->firstchild; child; child = child->nextchild)
842 : {
843 16460 : if (owner == child->nextchild)
844 : {
845 14622 : child->nextchild = owner->nextchild;
846 14622 : break;
847 : }
848 : }
849 : }
850 : }
851 :
852 1118018 : if (newparent)
853 : {
854 : Assert(owner != newparent);
855 74 : owner->parent = newparent;
856 74 : owner->nextchild = newparent->firstchild;
857 74 : newparent->firstchild = owner;
858 : }
859 : else
860 : {
861 1117944 : owner->parent = NULL;
862 1117944 : owner->nextchild = NULL;
863 : }
864 1118018 : }
865 :
866 : /*
867 : * Register or deregister callback functions for resource cleanup
868 : *
869 : * These functions are intended for use by dynamically loaded modules.
870 : * For built-in modules we generally just hardwire the appropriate calls.
871 : *
872 : * Note that the callback occurs post-commit or post-abort, so the callback
873 : * functions can only do noncritical cleanup.
874 : */
875 : void
876 46 : RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
877 : {
878 : ResourceReleaseCallbackItem *item;
879 :
880 : item = (ResourceReleaseCallbackItem *)
881 46 : MemoryContextAlloc(TopMemoryContext,
882 : sizeof(ResourceReleaseCallbackItem));
883 46 : item->callback = callback;
884 46 : item->arg = arg;
885 46 : item->next = ResourceRelease_callbacks;
886 46 : ResourceRelease_callbacks = item;
887 46 : }
888 :
889 : void
890 0 : UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
891 : {
892 : ResourceReleaseCallbackItem *item;
893 : ResourceReleaseCallbackItem *prev;
894 :
895 0 : prev = NULL;
896 0 : for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
897 : {
898 0 : if (item->callback == callback && item->arg == arg)
899 : {
900 0 : if (prev)
901 0 : prev->next = item->next;
902 : else
903 0 : ResourceRelease_callbacks = item->next;
904 0 : pfree(item);
905 0 : break;
906 : }
907 : }
908 0 : }
909 :
910 : /*
911 : * Establish an AuxProcessResourceOwner for the current process.
912 : */
913 : void
914 3694 : CreateAuxProcessResourceOwner(void)
915 : {
916 : Assert(AuxProcessResourceOwner == NULL);
917 : Assert(CurrentResourceOwner == NULL);
918 3694 : AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
919 3694 : CurrentResourceOwner = AuxProcessResourceOwner;
920 :
921 : /*
922 : * Register a shmem-exit callback for cleanup of aux-process resource
923 : * owner. (This needs to run after, e.g., ShutdownXLOG.)
924 : */
925 3694 : on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
926 3694 : }
927 :
928 : /*
929 : * Convenience routine to release all resources tracked in
930 : * AuxProcessResourceOwner (but that resowner is not destroyed here).
931 : * Warn about leaked resources if isCommit is true.
932 : */
933 : void
934 3836 : ReleaseAuxProcessResources(bool isCommit)
935 : {
936 : /*
937 : * At this writing, the only thing that could actually get released is
938 : * buffer pins; but we may as well do the full release protocol.
939 : */
940 3836 : ResourceOwnerRelease(AuxProcessResourceOwner,
941 : RESOURCE_RELEASE_BEFORE_LOCKS,
942 : isCommit, true);
943 3836 : ResourceOwnerRelease(AuxProcessResourceOwner,
944 : RESOURCE_RELEASE_LOCKS,
945 : isCommit, true);
946 3836 : ResourceOwnerRelease(AuxProcessResourceOwner,
947 : RESOURCE_RELEASE_AFTER_LOCKS,
948 : isCommit, true);
949 3836 : }
950 :
951 : /*
952 : * Shmem-exit callback for the same.
953 : * Warn about leaked resources if process exit code is zero (ie normal).
954 : */
955 : static void
956 3694 : ReleaseAuxProcessResourcesCallback(int code, Datum arg)
957 : {
958 3694 : bool isCommit = (code == 0);
959 :
960 3694 : ReleaseAuxProcessResources(isCommit);
961 3694 : }
962 :
963 :
964 : /*
965 : * Make sure there is room for at least one more entry in a ResourceOwner's
966 : * buffer array.
967 : *
968 : * This is separate from actually inserting an entry because if we run out
969 : * of memory, it's critical to do so *before* acquiring the resource.
970 : */
971 : void
972 110079532 : ResourceOwnerEnlargeBuffers(ResourceOwner owner)
973 : {
974 : /* We used to allow pinning buffers without a resowner, but no more */
975 : Assert(owner != NULL);
976 110079532 : ResourceArrayEnlarge(&(owner->bufferarr));
977 110079532 : }
978 :
979 : /*
980 : * Remember that a buffer pin is owned by a ResourceOwner
981 : *
982 : * Caller must have previously done ResourceOwnerEnlargeBuffers()
983 : */
984 : void
985 107750450 : ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
986 : {
987 107750450 : ResourceArrayAdd(&(owner->bufferarr), BufferGetDatum(buffer));
988 107750450 : }
989 :
990 : /*
991 : * Forget that a buffer pin is owned by a ResourceOwner
992 : */
993 : void
994 107750450 : ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
995 : {
996 107750450 : if (!ResourceArrayRemove(&(owner->bufferarr), BufferGetDatum(buffer)))
997 0 : elog(ERROR, "buffer %d is not owned by resource owner %s",
998 : buffer, owner->name);
999 107750450 : }
1000 :
1001 :
1002 : /*
1003 : * Make sure there is room for at least one more entry in a ResourceOwner's
1004 : * buffer array.
1005 : *
1006 : * This is separate from actually inserting an entry because if we run out
1007 : * of memory, it's critical to do so *before* acquiring the resource.
1008 : */
1009 : void
1010 3590748 : ResourceOwnerEnlargeBufferIOs(ResourceOwner owner)
1011 : {
1012 : /* We used to allow pinning buffers without a resowner, but no more */
1013 : Assert(owner != NULL);
1014 3590748 : ResourceArrayEnlarge(&(owner->bufferioarr));
1015 3590748 : }
1016 :
1017 : /*
1018 : * Remember that a buffer IO is owned by a ResourceOwner
1019 : *
1020 : * Caller must have previously done ResourceOwnerEnlargeBufferIOs()
1021 : */
1022 : void
1023 3590546 : ResourceOwnerRememberBufferIO(ResourceOwner owner, Buffer buffer)
1024 : {
1025 3590546 : ResourceArrayAdd(&(owner->bufferioarr), BufferGetDatum(buffer));
1026 3590546 : }
1027 :
1028 : /*
1029 : * Forget that a buffer IO is owned by a ResourceOwner
1030 : */
1031 : void
1032 3590546 : ResourceOwnerForgetBufferIO(ResourceOwner owner, Buffer buffer)
1033 : {
1034 3590546 : if (!ResourceArrayRemove(&(owner->bufferioarr), BufferGetDatum(buffer)))
1035 0 : elog(PANIC, "buffer IO %d is not owned by resource owner %s",
1036 : buffer, owner->name);
1037 3590546 : }
1038 :
1039 : /*
1040 : * Remember that a Local Lock is owned by a ResourceOwner
1041 : *
1042 : * This is different from the other Remember functions in that the list of
1043 : * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
1044 : * and when it overflows, we stop tracking locks. The point of only remembering
1045 : * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
1046 : * ResourceOwnerForgetLock doesn't need to scan through a large array to find
1047 : * the entry.
1048 : */
1049 : void
1050 24853030 : ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
1051 : {
1052 : Assert(locallock != NULL);
1053 :
1054 24853030 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
1055 2655870 : return; /* we have already overflowed */
1056 :
1057 22197160 : if (owner->nlocks < MAX_RESOWNER_LOCKS)
1058 22181798 : owner->locks[owner->nlocks] = locallock;
1059 : else
1060 : {
1061 : /* overflowed */
1062 : }
1063 22197160 : owner->nlocks++;
1064 : }
1065 :
1066 : /*
1067 : * Forget that a Local Lock is owned by a ResourceOwner
1068 : */
1069 : void
1070 24853030 : ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
1071 : {
1072 : int i;
1073 :
1074 24853030 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
1075 2901662 : return; /* we have overflowed */
1076 :
1077 : Assert(owner->nlocks > 0);
1078 24957340 : for (i = owner->nlocks - 1; i >= 0; i--)
1079 : {
1080 24957340 : if (locallock == owner->locks[i])
1081 : {
1082 21951368 : owner->locks[i] = owner->locks[owner->nlocks - 1];
1083 21951368 : owner->nlocks--;
1084 21951368 : return;
1085 : }
1086 : }
1087 0 : elog(ERROR, "lock reference %p is not owned by resource owner %s",
1088 : locallock, owner->name);
1089 : }
1090 :
1091 : /*
1092 : * Make sure there is room for at least one more entry in a ResourceOwner's
1093 : * catcache reference array.
1094 : *
1095 : * This is separate from actually inserting an entry because if we run out
1096 : * of memory, it's critical to do so *before* acquiring the resource.
1097 : */
1098 : void
1099 61066002 : ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
1100 : {
1101 61066002 : ResourceArrayEnlarge(&(owner->catrefarr));
1102 61066002 : }
1103 :
1104 : /*
1105 : * Remember that a catcache reference is owned by a ResourceOwner
1106 : *
1107 : * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs()
1108 : */
1109 : void
1110 61066002 : ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
1111 : {
1112 61066002 : ResourceArrayAdd(&(owner->catrefarr), PointerGetDatum(tuple));
1113 61066002 : }
1114 :
1115 : /*
1116 : * Forget that a catcache reference is owned by a ResourceOwner
1117 : */
1118 : void
1119 61066002 : ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
1120 : {
1121 61066002 : if (!ResourceArrayRemove(&(owner->catrefarr), PointerGetDatum(tuple)))
1122 0 : elog(ERROR, "catcache reference %p is not owned by resource owner %s",
1123 : tuple, owner->name);
1124 61066002 : }
1125 :
1126 : /*
1127 : * Make sure there is room for at least one more entry in a ResourceOwner's
1128 : * catcache-list reference array.
1129 : *
1130 : * This is separate from actually inserting an entry because if we run out
1131 : * of memory, it's critical to do so *before* acquiring the resource.
1132 : */
1133 : void
1134 2618836 : ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
1135 : {
1136 2618836 : ResourceArrayEnlarge(&(owner->catlistrefarr));
1137 2618836 : }
1138 :
1139 : /*
1140 : * Remember that a catcache-list reference is owned by a ResourceOwner
1141 : *
1142 : * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs()
1143 : */
1144 : void
1145 2618836 : ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
1146 : {
1147 2618836 : ResourceArrayAdd(&(owner->catlistrefarr), PointerGetDatum(list));
1148 2618836 : }
1149 :
1150 : /*
1151 : * Forget that a catcache-list reference is owned by a ResourceOwner
1152 : */
1153 : void
1154 2618836 : ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
1155 : {
1156 2618836 : if (!ResourceArrayRemove(&(owner->catlistrefarr), PointerGetDatum(list)))
1157 0 : elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
1158 : list, owner->name);
1159 2618836 : }
1160 :
1161 : /*
1162 : * Make sure there is room for at least one more entry in a ResourceOwner's
1163 : * relcache reference array.
1164 : *
1165 : * This is separate from actually inserting an entry because if we run out
1166 : * of memory, it's critical to do so *before* acquiring the resource.
1167 : */
1168 : void
1169 41730240 : ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
1170 : {
1171 41730240 : ResourceArrayEnlarge(&(owner->relrefarr));
1172 41730240 : }
1173 :
1174 : /*
1175 : * Remember that a relcache reference is owned by a ResourceOwner
1176 : *
1177 : * Caller must have previously done ResourceOwnerEnlargeRelationRefs()
1178 : */
1179 : void
1180 41376902 : ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
1181 : {
1182 41376902 : ResourceArrayAdd(&(owner->relrefarr), PointerGetDatum(rel));
1183 41376902 : }
1184 :
1185 : /*
1186 : * Forget that a relcache reference is owned by a ResourceOwner
1187 : */
1188 : void
1189 41376902 : ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
1190 : {
1191 41376902 : if (!ResourceArrayRemove(&(owner->relrefarr), PointerGetDatum(rel)))
1192 0 : elog(ERROR, "relcache reference %s is not owned by resource owner %s",
1193 : RelationGetRelationName(rel), owner->name);
1194 41376902 : }
1195 :
1196 : /*
1197 : * Debugging subroutine
1198 : */
1199 : static void
1200 0 : PrintRelCacheLeakWarning(Relation rel)
1201 : {
1202 0 : elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
1203 : RelationGetRelationName(rel));
1204 0 : }
1205 :
1206 : /*
1207 : * Make sure there is room for at least one more entry in a ResourceOwner's
1208 : * plancache reference array.
1209 : *
1210 : * This is separate from actually inserting an entry because if we run out
1211 : * of memory, it's critical to do so *before* acquiring the resource.
1212 : */
1213 : void
1214 172462 : ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
1215 : {
1216 172462 : ResourceArrayEnlarge(&(owner->planrefarr));
1217 172462 : }
1218 :
1219 : /*
1220 : * Remember that a plancache reference is owned by a ResourceOwner
1221 : *
1222 : * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
1223 : */
1224 : void
1225 172462 : ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
1226 : {
1227 172462 : ResourceArrayAdd(&(owner->planrefarr), PointerGetDatum(plan));
1228 172462 : }
1229 :
1230 : /*
1231 : * Forget that a plancache reference is owned by a ResourceOwner
1232 : */
1233 : void
1234 172462 : ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
1235 : {
1236 172462 : if (!ResourceArrayRemove(&(owner->planrefarr), PointerGetDatum(plan)))
1237 0 : elog(ERROR, "plancache reference %p is not owned by resource owner %s",
1238 : plan, owner->name);
1239 172462 : }
1240 :
1241 : /*
1242 : * Debugging subroutine
1243 : */
1244 : static void
1245 0 : PrintPlanCacheLeakWarning(CachedPlan *plan)
1246 : {
1247 0 : elog(WARNING, "plancache reference leak: plan %p not closed", plan);
1248 0 : }
1249 :
1250 : /*
1251 : * Make sure there is room for at least one more entry in a ResourceOwner's
1252 : * tupdesc reference array.
1253 : *
1254 : * This is separate from actually inserting an entry because if we run out
1255 : * of memory, it's critical to do so *before* acquiring the resource.
1256 : */
1257 : void
1258 25453662 : ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
1259 : {
1260 25453662 : ResourceArrayEnlarge(&(owner->tupdescarr));
1261 25453662 : }
1262 :
1263 : /*
1264 : * Remember that a tupdesc reference is owned by a ResourceOwner
1265 : *
1266 : * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
1267 : */
1268 : void
1269 25453662 : ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
1270 : {
1271 25453662 : ResourceArrayAdd(&(owner->tupdescarr), PointerGetDatum(tupdesc));
1272 25453662 : }
1273 :
1274 : /*
1275 : * Forget that a tupdesc reference is owned by a ResourceOwner
1276 : */
1277 : void
1278 25453662 : ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
1279 : {
1280 25453662 : if (!ResourceArrayRemove(&(owner->tupdescarr), PointerGetDatum(tupdesc)))
1281 0 : elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
1282 : tupdesc, owner->name);
1283 25453662 : }
1284 :
1285 : /*
1286 : * Debugging subroutine
1287 : */
1288 : static void
1289 0 : PrintTupleDescLeakWarning(TupleDesc tupdesc)
1290 : {
1291 0 : elog(WARNING,
1292 : "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
1293 : tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
1294 0 : }
1295 :
1296 : /*
1297 : * Make sure there is room for at least one more entry in a ResourceOwner's
1298 : * snapshot reference array.
1299 : *
1300 : * This is separate from actually inserting an entry because if we run out
1301 : * of memory, it's critical to do so *before* acquiring the resource.
1302 : */
1303 : void
1304 9730576 : ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
1305 : {
1306 9730576 : ResourceArrayEnlarge(&(owner->snapshotarr));
1307 9730576 : }
1308 :
1309 : /*
1310 : * Remember that a snapshot reference is owned by a ResourceOwner
1311 : *
1312 : * Caller must have previously done ResourceOwnerEnlargeSnapshots()
1313 : */
1314 : void
1315 9730576 : ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
1316 : {
1317 9730576 : ResourceArrayAdd(&(owner->snapshotarr), PointerGetDatum(snapshot));
1318 9730576 : }
1319 :
1320 : /*
1321 : * Forget that a snapshot reference is owned by a ResourceOwner
1322 : */
1323 : void
1324 9730576 : ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
1325 : {
1326 9730576 : if (!ResourceArrayRemove(&(owner->snapshotarr), PointerGetDatum(snapshot)))
1327 0 : elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
1328 : snapshot, owner->name);
1329 9730576 : }
1330 :
1331 : /*
1332 : * Debugging subroutine
1333 : */
1334 : static void
1335 0 : PrintSnapshotLeakWarning(Snapshot snapshot)
1336 : {
1337 0 : elog(WARNING, "Snapshot reference leak: Snapshot %p still referenced",
1338 : snapshot);
1339 0 : }
1340 :
1341 :
1342 : /*
1343 : * Make sure there is room for at least one more entry in a ResourceOwner's
1344 : * files reference array.
1345 : *
1346 : * This is separate from actually inserting an entry because if we run out
1347 : * of memory, it's critical to do so *before* acquiring the resource.
1348 : */
1349 : void
1350 12816 : ResourceOwnerEnlargeFiles(ResourceOwner owner)
1351 : {
1352 12816 : ResourceArrayEnlarge(&(owner->filearr));
1353 12816 : }
1354 :
1355 : /*
1356 : * Remember that a temporary file is owned by a ResourceOwner
1357 : *
1358 : * Caller must have previously done ResourceOwnerEnlargeFiles()
1359 : */
1360 : void
1361 8654 : ResourceOwnerRememberFile(ResourceOwner owner, File file)
1362 : {
1363 8654 : ResourceArrayAdd(&(owner->filearr), FileGetDatum(file));
1364 8654 : }
1365 :
1366 : /*
1367 : * Forget that a temporary file is owned by a ResourceOwner
1368 : */
1369 : void
1370 8654 : ResourceOwnerForgetFile(ResourceOwner owner, File file)
1371 : {
1372 8654 : if (!ResourceArrayRemove(&(owner->filearr), FileGetDatum(file)))
1373 0 : elog(ERROR, "temporary file %d is not owned by resource owner %s",
1374 : file, owner->name);
1375 8654 : }
1376 :
1377 : /*
1378 : * Debugging subroutine
1379 : */
1380 : static void
1381 0 : PrintFileLeakWarning(File file)
1382 : {
1383 0 : elog(WARNING, "temporary file leak: File %d still referenced",
1384 : file);
1385 0 : }
1386 :
1387 : /*
1388 : * Make sure there is room for at least one more entry in a ResourceOwner's
1389 : * dynamic shmem segment reference array.
1390 : *
1391 : * This is separate from actually inserting an entry because if we run out
1392 : * of memory, it's critical to do so *before* acquiring the resource.
1393 : */
1394 : void
1395 29584 : ResourceOwnerEnlargeDSMs(ResourceOwner owner)
1396 : {
1397 29584 : ResourceArrayEnlarge(&(owner->dsmarr));
1398 29584 : }
1399 :
1400 : /*
1401 : * Remember that a dynamic shmem segment is owned by a ResourceOwner
1402 : *
1403 : * Caller must have previously done ResourceOwnerEnlargeDSMs()
1404 : */
1405 : void
1406 29584 : ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
1407 : {
1408 29584 : ResourceArrayAdd(&(owner->dsmarr), PointerGetDatum(seg));
1409 29584 : }
1410 :
1411 : /*
1412 : * Forget that a dynamic shmem segment is owned by a ResourceOwner
1413 : */
1414 : void
1415 29584 : ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
1416 : {
1417 29584 : if (!ResourceArrayRemove(&(owner->dsmarr), PointerGetDatum(seg)))
1418 0 : elog(ERROR, "dynamic shared memory segment %u is not owned by resource owner %s",
1419 : dsm_segment_handle(seg), owner->name);
1420 29584 : }
1421 :
1422 : /*
1423 : * Debugging subroutine
1424 : */
1425 : static void
1426 0 : PrintDSMLeakWarning(dsm_segment *seg)
1427 : {
1428 0 : elog(WARNING, "dynamic shared memory leak: segment %u still referenced",
1429 : dsm_segment_handle(seg));
1430 0 : }
1431 :
1432 : /*
1433 : * Make sure there is room for at least one more entry in a ResourceOwner's
1434 : * JIT context reference array.
1435 : *
1436 : * This is separate from actually inserting an entry because if we run out of
1437 : * memory, it's critical to do so *before* acquiring the resource.
1438 : */
1439 : void
1440 1664 : ResourceOwnerEnlargeJIT(ResourceOwner owner)
1441 : {
1442 1664 : ResourceArrayEnlarge(&(owner->jitarr));
1443 1664 : }
1444 :
1445 : /*
1446 : * Remember that a JIT context is owned by a ResourceOwner
1447 : *
1448 : * Caller must have previously done ResourceOwnerEnlargeJIT()
1449 : */
1450 : void
1451 1664 : ResourceOwnerRememberJIT(ResourceOwner owner, Datum handle)
1452 : {
1453 1664 : ResourceArrayAdd(&(owner->jitarr), handle);
1454 1664 : }
1455 :
1456 : /*
1457 : * Forget that a JIT context is owned by a ResourceOwner
1458 : */
1459 : void
1460 1664 : ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
1461 : {
1462 1664 : if (!ResourceArrayRemove(&(owner->jitarr), handle))
1463 0 : elog(ERROR, "JIT context %p is not owned by resource owner %s",
1464 : DatumGetPointer(handle), owner->name);
1465 1664 : }
1466 :
1467 : /*
1468 : * Make sure there is room for at least one more entry in a ResourceOwner's
1469 : * cryptohash context reference array.
1470 : *
1471 : * This is separate from actually inserting an entry because if we run out of
1472 : * memory, it's critical to do so *before* acquiring the resource.
1473 : */
1474 : void
1475 852410 : ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
1476 : {
1477 852410 : ResourceArrayEnlarge(&(owner->cryptohasharr));
1478 852410 : }
1479 :
1480 : /*
1481 : * Remember that a cryptohash context is owned by a ResourceOwner
1482 : *
1483 : * Caller must have previously done ResourceOwnerEnlargeCryptoHash()
1484 : */
1485 : void
1486 852410 : ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
1487 : {
1488 852410 : ResourceArrayAdd(&(owner->cryptohasharr), handle);
1489 852410 : }
1490 :
1491 : /*
1492 : * Forget that a cryptohash context is owned by a ResourceOwner
1493 : */
1494 : void
1495 852400 : ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
1496 : {
1497 852400 : if (!ResourceArrayRemove(&(owner->cryptohasharr), handle))
1498 0 : elog(ERROR, "cryptohash context %p is not owned by resource owner %s",
1499 : DatumGetPointer(handle), owner->name);
1500 852400 : }
1501 :
1502 : /*
1503 : * Debugging subroutine
1504 : */
1505 : static void
1506 0 : PrintCryptoHashLeakWarning(Datum handle)
1507 : {
1508 0 : elog(WARNING, "cryptohash context reference leak: context %p still referenced",
1509 : DatumGetPointer(handle));
1510 0 : }
1511 :
1512 : /*
1513 : * Make sure there is room for at least one more entry in a ResourceOwner's
1514 : * hmac context reference array.
1515 : *
1516 : * This is separate from actually inserting an entry because if we run out of
1517 : * memory, it's critical to do so *before* acquiring the resource.
1518 : */
1519 : void
1520 454 : ResourceOwnerEnlargeHMAC(ResourceOwner owner)
1521 : {
1522 454 : ResourceArrayEnlarge(&(owner->hmacarr));
1523 454 : }
1524 :
1525 : /*
1526 : * Remember that a HMAC context is owned by a ResourceOwner
1527 : *
1528 : * Caller must have previously done ResourceOwnerEnlargeHMAC()
1529 : */
1530 : void
1531 454 : ResourceOwnerRememberHMAC(ResourceOwner owner, Datum handle)
1532 : {
1533 454 : ResourceArrayAdd(&(owner->hmacarr), handle);
1534 454 : }
1535 :
1536 : /*
1537 : * Forget that a HMAC context is owned by a ResourceOwner
1538 : */
1539 : void
1540 454 : ResourceOwnerForgetHMAC(ResourceOwner owner, Datum handle)
1541 : {
1542 454 : if (!ResourceArrayRemove(&(owner->hmacarr), handle))
1543 0 : elog(ERROR, "HMAC context %p is not owned by resource owner %s",
1544 : DatumGetPointer(handle), owner->name);
1545 454 : }
1546 :
1547 : /*
1548 : * Debugging subroutine
1549 : */
1550 : static void
1551 0 : PrintHMACLeakWarning(Datum handle)
1552 : {
1553 0 : elog(WARNING, "HMAC context reference leak: context %p still referenced",
1554 : DatumGetPointer(handle));
1555 0 : }
|