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