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 26193206 : 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 26193206 : resarr->invalidval = invalidval;
205 : /* We don't allocate any storage until needed */
206 26193206 : }
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 401399082 : ResourceArrayEnlarge(ResourceArray *resarr)
216 : {
217 : uint32 i,
218 : oldcap,
219 : newcap;
220 : Datum *olditemsarr;
221 : Datum *newitemsarr;
222 :
223 401399082 : if (resarr->nitems < resarr->maxitems)
224 394593040 : return; /* no work needed */
225 :
226 6806042 : olditemsarr = resarr->itemsarr;
227 6806042 : oldcap = resarr->capacity;
228 :
229 : /* Double the capacity of the array (capacity must stay a power of 2!) */
230 6806042 : newcap = (oldcap > 0) ? oldcap * 2 : RESARRAY_INIT_SIZE;
231 6806042 : newitemsarr = (Datum *) MemoryContextAlloc(TopMemoryContext,
232 : newcap * sizeof(Datum));
233 119007354 : for (i = 0; i < newcap; i++)
234 112201312 : newitemsarr[i] = resarr->invalidval;
235 :
236 : /* We assume we can't fail below this point, so OK to scribble on resarr */
237 6806042 : resarr->itemsarr = newitemsarr;
238 6806042 : resarr->capacity = newcap;
239 6806042 : resarr->maxitems = RESARRAY_MAX_ITEMS(newcap);
240 6806042 : resarr->nitems = 0;
241 :
242 6806042 : 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 1897084 : for (i = 0; i < oldcap; i++)
252 : {
253 1869888 : if (olditemsarr[i] != resarr->invalidval)
254 1531776 : ResourceArrayAdd(resarr, olditemsarr[i]);
255 : }
256 :
257 : /* And release old array. */
258 27196 : 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 396845108 : ResourceArrayAdd(ResourceArray *resarr, Datum value)
271 : {
272 : uint32 idx;
273 :
274 : Assert(value != resarr->invalidval);
275 : Assert(resarr->nitems < resarr->maxitems);
276 :
277 396845108 : if (RESARRAY_IS_ARRAY(resarr))
278 : {
279 : /* Append to linear array. */
280 394488862 : idx = resarr->nitems;
281 : }
282 : else
283 : {
284 : /* Insert into first free slot at or after hash location. */
285 2356246 : uint32 mask = resarr->capacity - 1;
286 :
287 2356246 : idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask;
288 : for (;;)
289 : {
290 781618844 : if (resarr->itemsarr[idx] == resarr->invalidval)
291 2356246 : break;
292 779262598 : idx = (idx + 1) & mask;
293 : }
294 : }
295 396845108 : resarr->lastidx = idx;
296 396845108 : resarr->itemsarr[idx] = value;
297 396845108 : resarr->nitems++;
298 396845108 : }
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 395313322 : ResourceArrayRemove(ResourceArray *resarr, Datum value)
309 : {
310 : uint32 i,
311 : idx,
312 395313322 : lastidx = resarr->lastidx;
313 :
314 : Assert(value != resarr->invalidval);
315 :
316 : /* Search through all items, but try lastidx first. */
317 395313322 : if (RESARRAY_IS_ARRAY(resarr))
318 : {
319 393971412 : if (lastidx < resarr->nitems &&
320 393971412 : resarr->itemsarr[lastidx] == value)
321 : {
322 382813102 : resarr->itemsarr[lastidx] = resarr->itemsarr[resarr->nitems - 1];
323 382813102 : resarr->nitems--;
324 : /* Update lastidx to make reverse-order removals fast. */
325 382813102 : resarr->lastidx = resarr->nitems - 1;
326 382813102 : return true;
327 : }
328 24355688 : for (i = 0; i < resarr->nitems; i++)
329 : {
330 24355688 : if (resarr->itemsarr[i] == value)
331 : {
332 11158310 : resarr->itemsarr[i] = resarr->itemsarr[resarr->nitems - 1];
333 11158310 : resarr->nitems--;
334 : /* Update lastidx to make reverse-order removals fast. */
335 11158310 : resarr->lastidx = resarr->nitems - 1;
336 11158310 : return true;
337 : }
338 : }
339 : }
340 : else
341 : {
342 1341910 : uint32 mask = resarr->capacity - 1;
343 :
344 1341910 : if (lastidx < resarr->capacity &&
345 1341910 : resarr->itemsarr[lastidx] == value)
346 : {
347 396572 : resarr->itemsarr[lastidx] = resarr->invalidval;
348 396572 : resarr->nitems--;
349 396572 : return true;
350 : }
351 945338 : idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask;
352 342303458 : for (i = 0; i < resarr->capacity; i++)
353 : {
354 342303458 : if (resarr->itemsarr[idx] == value)
355 : {
356 945338 : resarr->itemsarr[idx] = resarr->invalidval;
357 945338 : resarr->nitems--;
358 945338 : return true;
359 : }
360 341358120 : 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 26396890 : ResourceArrayGetAny(ResourceArray *resarr, Datum *value)
378 : {
379 26396890 : if (resarr->nitems == 0)
380 26211680 : return false;
381 :
382 185210 : if (RESARRAY_IS_ARRAY(resarr))
383 : {
384 : /* Linear array: just return the first element. */
385 167244 : 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 44314 : resarr->lastidx &= mask;
395 44314 : if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
396 17966 : break;
397 26348 : resarr->lastidx++;
398 : }
399 : }
400 :
401 185210 : *value = resarr->itemsarr[resarr->lastidx];
402 185210 : return true;
403 : }
404 :
405 : /*
406 : * Trash a ResourceArray (we don't care about its state after this)
407 : */
408 : static void
409 26131690 : ResourceArrayFree(ResourceArray *resarr)
410 : {
411 26131690 : if (resarr->itemsarr)
412 6771376 : pfree(resarr->itemsarr);
413 26131690 : }
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 2014862 : ResourceOwnerCreate(ResourceOwner parent, const char *name)
430 : {
431 : ResourceOwner owner;
432 :
433 2014862 : owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
434 : sizeof(ResourceOwnerData));
435 2014862 : owner->name = name;
436 :
437 2014862 : if (parent)
438 : {
439 1032484 : owner->parent = parent;
440 1032484 : owner->nextchild = parent->firstchild;
441 1032484 : parent->firstchild = owner;
442 : }
443 :
444 2014862 : ResourceArrayInit(&(owner->bufferarr), BufferGetDatum(InvalidBuffer));
445 2014862 : ResourceArrayInit(&(owner->bufferioarr), BufferGetDatum(InvalidBuffer));
446 2014862 : ResourceArrayInit(&(owner->catrefarr), PointerGetDatum(NULL));
447 2014862 : ResourceArrayInit(&(owner->catlistrefarr), PointerGetDatum(NULL));
448 2014862 : ResourceArrayInit(&(owner->relrefarr), PointerGetDatum(NULL));
449 2014862 : ResourceArrayInit(&(owner->planrefarr), PointerGetDatum(NULL));
450 2014862 : ResourceArrayInit(&(owner->tupdescarr), PointerGetDatum(NULL));
451 2014862 : ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
452 2014862 : ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
453 2014862 : ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
454 2014862 : ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
455 2014862 : ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL));
456 2014862 : ResourceArrayInit(&(owner->hmacarr), PointerGetDatum(NULL));
457 :
458 2014862 : 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 5868024 : ResourceOwnerRelease(ResourceOwner owner,
489 : ResourceReleasePhase phase,
490 : bool isCommit,
491 : bool isTopLevel)
492 : {
493 : /* There's not currently any setup needed before recursing */
494 5868024 : ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
495 5868024 : }
496 :
497 : static void
498 6045486 : 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 6222948 : for (child = owner->firstchild; child != NULL; child = child->nextchild)
511 177462 : ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
512 :
513 : /*
514 : * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
515 : * get confused.
516 : */
517 6045486 : save = CurrentResourceOwner;
518 6045486 : CurrentResourceOwner = owner;
519 :
520 6045486 : 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 2015188 : 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 2022466 : while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
550 : {
551 7304 : Buffer res = DatumGetBuffer(foundres);
552 :
553 7304 : if (isCommit)
554 0 : PrintBufferLeakWarning(res);
555 7304 : ReleaseBuffer(res);
556 : }
557 :
558 : /* Ditto for relcache references */
559 2046550 : while (ResourceArrayGetAny(&(owner->relrefarr), &foundres))
560 : {
561 31388 : Relation res = (Relation) DatumGetPointer(foundres);
562 :
563 31388 : if (isCommit)
564 0 : PrintRelCacheLeakWarning(res);
565 31388 : RelationClose(res);
566 : }
567 :
568 : /* Ditto for dynamic shared memory segments */
569 2015162 : 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 2015178 : 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 2015170 : 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 2015162 : 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 4030324 : else if (phase == RESOURCE_RELEASE_LOCKS)
608 : {
609 2015162 : 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 1041586 : if (owner == TopTransactionResourceOwner)
617 : {
618 976504 : ProcReleaseLocks(isCommit);
619 976504 : 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 973576 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
639 : {
640 6876 : locks = NULL;
641 6876 : nlocks = 0;
642 : }
643 : else
644 : {
645 966700 : locks = owner->locks;
646 966700 : nlocks = owner->nlocks;
647 : }
648 :
649 973576 : if (isCommit)
650 964346 : LockReassignCurrentOwner(locks, nlocks);
651 : else
652 9230 : LockReleaseCurrentOwner(locks, nlocks);
653 : }
654 : }
655 2015162 : 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 2024392 : while (ResourceArrayGetAny(&(owner->catrefarr), &foundres))
665 : {
666 9230 : HeapTuple res = (HeapTuple) DatumGetPointer(foundres);
667 :
668 9230 : if (isCommit)
669 0 : PrintCatCacheLeakWarning(res);
670 9230 : ReleaseCatCache(res);
671 : }
672 :
673 : /* Ditto for catcache lists */
674 2015198 : 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 2025294 : while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
685 : {
686 10132 : CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);
687 :
688 10132 : if (isCommit)
689 0 : PrintPlanCacheLeakWarning(res);
690 10132 : ReleaseCachedPlan(res, owner);
691 : }
692 :
693 : /* Ditto for tupdesc references */
694 2026202 : while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres))
695 : {
696 11040 : TupleDesc res = (TupleDesc) DatumGetPointer(foundres);
697 :
698 11040 : if (isCommit)
699 0 : PrintTupleDescLeakWarning(res);
700 11040 : DecrTupleDescRefCount(res);
701 : }
702 :
703 : /* Ditto for snapshot references */
704 2065534 : while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres))
705 : {
706 50372 : Snapshot res = (Snapshot) DatumGetPointer(foundres);
707 :
708 50372 : if (isCommit)
709 0 : PrintSnapshotLeakWarning(res);
710 50372 : UnregisterSnapshot(res);
711 : }
712 :
713 : /* Ditto for temporary files */
714 2015170 : 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 6049002 : 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 6045486 : CurrentResourceOwner = save;
733 6045486 : }
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 14574 : ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner)
744 : {
745 : Datum foundres;
746 :
747 80224 : while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
748 : {
749 65650 : CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);
750 :
751 65650 : ReleaseCachedPlan(res, owner);
752 : }
753 14574 : }
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 2010130 : 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 2069290 : while (owner->firstchild != NULL)
788 59160 : 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 2010130 : ResourceOwnerNewParent(owner, NULL);
796 :
797 : /* And free the object. */
798 2010130 : ResourceArrayFree(&(owner->bufferarr));
799 2010130 : ResourceArrayFree(&(owner->bufferioarr));
800 2010130 : ResourceArrayFree(&(owner->catrefarr));
801 2010130 : ResourceArrayFree(&(owner->catlistrefarr));
802 2010130 : ResourceArrayFree(&(owner->relrefarr));
803 2010130 : ResourceArrayFree(&(owner->planrefarr));
804 2010130 : ResourceArrayFree(&(owner->tupdescarr));
805 2010130 : ResourceArrayFree(&(owner->snapshotarr));
806 2010130 : ResourceArrayFree(&(owner->filearr));
807 2010130 : ResourceArrayFree(&(owner->dsmarr));
808 2010130 : ResourceArrayFree(&(owner->jitarr));
809 2010130 : ResourceArrayFree(&(owner->cryptohasharr));
810 2010130 : ResourceArrayFree(&(owner->hmacarr));
811 :
812 2010130 : pfree(owner);
813 2010130 : }
814 :
815 : /*
816 : * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
817 : */
818 : ResourceOwner
819 964346 : ResourceOwnerGetParent(ResourceOwner owner)
820 : {
821 964346 : return owner->parent;
822 : }
823 :
824 : /*
825 : * Reassign a ResourceOwner to have a new parent
826 : */
827 : void
828 2010200 : ResourceOwnerNewParent(ResourceOwner owner,
829 : ResourceOwner newparent)
830 : {
831 2010200 : ResourceOwner oldparent = owner->parent;
832 :
833 2010200 : if (oldparent)
834 : {
835 1032554 : if (owner == oldparent->firstchild)
836 1017944 : oldparent->firstchild = owner->nextchild;
837 : else
838 : {
839 : ResourceOwner child;
840 :
841 16448 : for (child = oldparent->firstchild; child; child = child->nextchild)
842 : {
843 16448 : if (owner == child->nextchild)
844 : {
845 14610 : child->nextchild = owner->nextchild;
846 14610 : break;
847 : }
848 : }
849 : }
850 : }
851 :
852 2010200 : if (newparent)
853 : {
854 : Assert(owner != newparent);
855 70 : owner->parent = newparent;
856 70 : owner->nextchild = newparent->firstchild;
857 70 : newparent->firstchild = owner;
858 : }
859 : else
860 : {
861 2010130 : owner->parent = NULL;
862 2010130 : owner->nextchild = NULL;
863 : }
864 2010200 : }
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 4722 : CreateAuxProcessResourceOwner(void)
915 : {
916 : Assert(AuxProcessResourceOwner == NULL);
917 : Assert(CurrentResourceOwner == NULL);
918 4722 : AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
919 4722 : 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 4722 : on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
926 4722 : }
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 5952 : 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 5952 : ResourceOwnerRelease(AuxProcessResourceOwner,
941 : RESOURCE_RELEASE_BEFORE_LOCKS,
942 : isCommit, true);
943 5952 : ResourceOwnerRelease(AuxProcessResourceOwner,
944 : RESOURCE_RELEASE_LOCKS,
945 : isCommit, true);
946 5952 : ResourceOwnerRelease(AuxProcessResourceOwner,
947 : RESOURCE_RELEASE_AFTER_LOCKS,
948 : isCommit, true);
949 5952 : }
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 4722 : ReleaseAuxProcessResourcesCallback(int code, Datum arg)
957 : {
958 4722 : bool isCommit = (code == 0);
959 :
960 4722 : ReleaseAuxProcessResources(isCommit);
961 4722 : }
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 163738960 : ResourceOwnerEnlargeBuffers(ResourceOwner owner)
973 : {
974 : /* We used to allow pinning buffers without a resowner, but no more */
975 : Assert(owner != NULL);
976 163738960 : ResourceArrayEnlarge(&(owner->bufferarr));
977 163738960 : }
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 161111040 : ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
986 : {
987 161111040 : ResourceArrayAdd(&(owner->bufferarr), BufferGetDatum(buffer));
988 161111040 : }
989 :
990 : /*
991 : * Forget that a buffer pin is owned by a ResourceOwner
992 : */
993 : void
994 161111040 : ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
995 : {
996 161111040 : if (!ResourceArrayRemove(&(owner->bufferarr), BufferGetDatum(buffer)))
997 0 : elog(ERROR, "buffer %d is not owned by resource owner %s",
998 : buffer, owner->name);
999 161111040 : }
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 5042792 : ResourceOwnerEnlargeBufferIOs(ResourceOwner owner)
1011 : {
1012 : /* We used to allow pinning buffers without a resowner, but no more */
1013 : Assert(owner != NULL);
1014 5042792 : ResourceArrayEnlarge(&(owner->bufferioarr));
1015 5042792 : }
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 5042638 : ResourceOwnerRememberBufferIO(ResourceOwner owner, Buffer buffer)
1024 : {
1025 5042638 : ResourceArrayAdd(&(owner->bufferioarr), BufferGetDatum(buffer));
1026 5042638 : }
1027 :
1028 : /*
1029 : * Forget that a buffer IO is owned by a ResourceOwner
1030 : */
1031 : void
1032 5042638 : ResourceOwnerForgetBufferIO(ResourceOwner owner, Buffer buffer)
1033 : {
1034 5042638 : 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 5042638 : }
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 50033976 : ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
1051 : {
1052 : Assert(locallock != NULL);
1053 :
1054 50033976 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
1055 3938092 : return; /* we have already overflowed */
1056 :
1057 46095884 : if (owner->nlocks < MAX_RESOWNER_LOCKS)
1058 46078914 : owner->locks[owner->nlocks] = locallock;
1059 : else
1060 : {
1061 : /* overflowed */
1062 : }
1063 46095884 : owner->nlocks++;
1064 : }
1065 :
1066 : /*
1067 : * Forget that a Local Lock is owned by a ResourceOwner
1068 : */
1069 : void
1070 50033976 : ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
1071 : {
1072 : int i;
1073 :
1074 50033976 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
1075 4209612 : return; /* we have overflowed */
1076 :
1077 : Assert(owner->nlocks > 0);
1078 52632350 : for (i = owner->nlocks - 1; i >= 0; i--)
1079 : {
1080 52632350 : if (locallock == owner->locks[i])
1081 : {
1082 45824364 : owner->locks[i] = owner->locks[owner->nlocks - 1];
1083 45824364 : owner->nlocks--;
1084 45824364 : 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 95248882 : ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
1100 : {
1101 95248882 : ResourceArrayEnlarge(&(owner->catrefarr));
1102 95248882 : }
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 95248882 : ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
1111 : {
1112 95248882 : ResourceArrayAdd(&(owner->catrefarr), PointerGetDatum(tuple));
1113 95248882 : }
1114 :
1115 : /*
1116 : * Forget that a catcache reference is owned by a ResourceOwner
1117 : */
1118 : void
1119 95248882 : ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
1120 : {
1121 95248882 : 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 95248882 : }
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 3119894 : ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
1135 : {
1136 3119894 : ResourceArrayEnlarge(&(owner->catlistrefarr));
1137 3119894 : }
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 3119894 : ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
1146 : {
1147 3119894 : ResourceArrayAdd(&(owner->catlistrefarr), PointerGetDatum(list));
1148 3119894 : }
1149 :
1150 : /*
1151 : * Forget that a catcache-list reference is owned by a ResourceOwner
1152 : */
1153 : void
1154 3119894 : ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
1155 : {
1156 3119894 : 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 3119894 : }
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 75941780 : ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
1170 : {
1171 75941780 : ResourceArrayEnlarge(&(owner->relrefarr));
1172 75941780 : }
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 72488186 : ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
1181 : {
1182 72488186 : ResourceArrayAdd(&(owner->relrefarr), PointerGetDatum(rel));
1183 72488186 : }
1184 :
1185 : /*
1186 : * Forget that a relcache reference is owned by a ResourceOwner
1187 : */
1188 : void
1189 72488186 : ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
1190 : {
1191 72488186 : 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 72488186 : }
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 168482 : ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
1215 : {
1216 168482 : ResourceArrayEnlarge(&(owner->planrefarr));
1217 168482 : }
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 168482 : ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
1226 : {
1227 168482 : ResourceArrayAdd(&(owner->planrefarr), PointerGetDatum(plan));
1228 168482 : }
1229 :
1230 : /*
1231 : * Forget that a plancache reference is owned by a ResourceOwner
1232 : */
1233 : void
1234 168482 : ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
1235 : {
1236 168482 : 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 168482 : }
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 40360496 : ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
1259 : {
1260 40360496 : ResourceArrayEnlarge(&(owner->tupdescarr));
1261 40360496 : }
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 40360496 : ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
1270 : {
1271 40360496 : ResourceArrayAdd(&(owner->tupdescarr), PointerGetDatum(tupdesc));
1272 40360496 : }
1273 :
1274 : /*
1275 : * Forget that a tupdesc reference is owned by a ResourceOwner
1276 : */
1277 : void
1278 40360496 : ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
1279 : {
1280 40360496 : 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 40360496 : }
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 16733928 : ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
1305 : {
1306 16733928 : ResourceArrayEnlarge(&(owner->snapshotarr));
1307 16733928 : }
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 16733928 : ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
1316 : {
1317 16733928 : ResourceArrayAdd(&(owner->snapshotarr), PointerGetDatum(snapshot));
1318 16733928 : }
1319 :
1320 : /*
1321 : * Forget that a snapshot reference is owned by a ResourceOwner
1322 : */
1323 : void
1324 16733928 : ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
1325 : {
1326 16733928 : 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 16733928 : }
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 12434 : ResourceOwnerEnlargeFiles(ResourceOwner owner)
1351 : {
1352 12434 : ResourceArrayEnlarge(&(owner->filearr));
1353 12434 : }
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 8352 : ResourceOwnerRememberFile(ResourceOwner owner, File file)
1362 : {
1363 8352 : ResourceArrayAdd(&(owner->filearr), FileGetDatum(file));
1364 8352 : }
1365 :
1366 : /*
1367 : * Forget that a temporary file is owned by a ResourceOwner
1368 : */
1369 : void
1370 8352 : ResourceOwnerForgetFile(ResourceOwner owner, File file)
1371 : {
1372 8352 : 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 8352 : }
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 30450 : ResourceOwnerEnlargeDSMs(ResourceOwner owner)
1396 : {
1397 30450 : ResourceArrayEnlarge(&(owner->dsmarr));
1398 30450 : }
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 30450 : ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
1407 : {
1408 30450 : ResourceArrayAdd(&(owner->dsmarr), PointerGetDatum(seg));
1409 30450 : }
1410 :
1411 : /*
1412 : * Forget that a dynamic shmem segment is owned by a ResourceOwner
1413 : */
1414 : void
1415 30450 : ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
1416 : {
1417 30450 : 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 30450 : }
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 1668 : ResourceOwnerEnlargeJIT(ResourceOwner owner)
1441 : {
1442 1668 : ResourceArrayEnlarge(&(owner->jitarr));
1443 1668 : }
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 1668 : ResourceOwnerRememberJIT(ResourceOwner owner, Datum handle)
1452 : {
1453 1668 : ResourceArrayAdd(&(owner->jitarr), handle);
1454 1668 : }
1455 :
1456 : /*
1457 : * Forget that a JIT context is owned by a ResourceOwner
1458 : */
1459 : void
1460 1668 : ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
1461 : {
1462 1668 : 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 1668 : }
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 998862 : ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
1476 : {
1477 998862 : ResourceArrayEnlarge(&(owner->cryptohasharr));
1478 998862 : }
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 998862 : ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
1487 : {
1488 998862 : ResourceArrayAdd(&(owner->cryptohasharr), handle);
1489 998862 : }
1490 :
1491 : /*
1492 : * Forget that a cryptohash context is owned by a ResourceOwner
1493 : */
1494 : void
1495 998852 : ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
1496 : {
1497 998852 : 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 998852 : }
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 : }
|