PostgreSQL Source Code git master
resowner.c
Go to the documentation of this file.
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 on how to use it.
10 *
11 * The implementation consists of a small fixed-size array and a hash table.
12 * New entries are inserted to the fixed-size array, and when the array
13 * fills up, all the entries are moved to the hash table. This way, the
14 * array always contains a few most recently remembered references. To find
15 * a particular reference, you need to search both the array and the hash
16 * table.
17 *
18 * The most frequent usage is that a resource is remembered, and forgotten
19 * shortly thereafter. For example, pin a buffer, read one tuple from it,
20 * release the pin. Linearly scanning the small array handles that case
21 * efficiently. However, some resources are held for a longer time, and
22 * sometimes a lot of resources need to be held simultaneously. The hash
23 * table handles those cases.
24 *
25 * When it's time to release the resources, we sort them according to the
26 * release-priority of each resource, and release them in that order.
27 *
28 * Local lock references are special, they are not stored in the array or
29 * the hash table. Instead, each resource owner has a separate small cache
30 * of locks it owns. The lock manager has the same information in its local
31 * lock hash table, and we fall back on that if the cache overflows, but
32 * traversing the hash table is slower when there are a lot of locks
33 * belonging to other resource owners. This is to speed up bulk releasing
34 * or reassigning locks from a resource owner to its parent.
35 *
36 *
37 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
38 * Portions Copyright (c) 1994, Regents of the University of California
39 *
40 *
41 * IDENTIFICATION
42 * src/backend/utils/resowner/resowner.c
43 *
44 *-------------------------------------------------------------------------
45 */
46#include "postgres.h"
47
48#include "common/hashfn.h"
49#include "common/int.h"
50#include "storage/ipc.h"
51#include "storage/predicate.h"
52#include "storage/proc.h"
53#include "utils/memutils.h"
54#include "utils/resowner.h"
55
56/*
57 * ResourceElem represents a reference associated with a resource owner.
58 *
59 * All objects managed by this code are required to fit into a Datum,
60 * which is fine since they are generally pointers or integers.
61 */
62typedef struct ResourceElem
63{
65 const ResourceOwnerDesc *kind; /* NULL indicates a free hash table slot */
67
68/*
69 * Size of the fixed-size array to hold most-recently remembered resources.
70 */
71#define RESOWNER_ARRAY_SIZE 32
72
73/*
74 * Initially allocated size of a ResourceOwner's hash table. Must be power of
75 * two because we use (capacity - 1) as mask for hashing.
76 */
77#define RESOWNER_HASH_INIT_SIZE 64
78
79/*
80 * How many items may be stored in a hash table of given capacity. When this
81 * number is reached, we must resize.
82 *
83 * The hash table must always have enough free space that we can copy the
84 * entries from the array to it, in ResourceOwnerSort. We also insist that
85 * the initial size is large enough that we don't hit the max size immediately
86 * when it's created. Aside from those limitations, 0.75 is a reasonable fill
87 * factor.
88 */
89#define RESOWNER_HASH_MAX_ITEMS(capacity) \
90 Min(capacity - RESOWNER_ARRAY_SIZE, (capacity)/4 * 3)
91
93 "initial hash size too small compared to array size");
94
95/*
96 * MAX_RESOWNER_LOCKS is the size of the per-resource owner locks cache. It's
97 * chosen based on some testing with pg_dump with a large schema. When the
98 * tests were done (on 9.2), resource owners in a pg_dump run contained up
99 * to 9 locks, regardless of the schema size, except for the top resource
100 * owner which contained much more (overflowing the cache). 15 seems like a
101 * nice round number that's somewhat higher than what pg_dump needs. Note that
102 * making this number larger is not free - the bigger the cache, the slower
103 * it is to release locks (in retail), when a resource owner holds many locks.
104 */
105#define MAX_RESOWNER_LOCKS 15
106
107/*
108 * ResourceOwner objects look like this
109 */
111{
112 ResourceOwner parent; /* NULL if no parent (toplevel owner) */
113 ResourceOwner firstchild; /* head of linked list of children */
114 ResourceOwner nextchild; /* next child of same parent */
115 const char *name; /* name (just for debugging) */
116
117 /*
118 * When ResourceOwnerRelease is called, we sort the 'hash' and 'arr' by
119 * the release priority. After that, no new resources can be remembered
120 * or forgotten in retail. We have separate flags because
121 * ResourceOwnerReleaseAllOfKind() temporarily sets 'releasing' without
122 * sorting the arrays.
123 */
125 bool sorted; /* are 'hash' and 'arr' sorted by priority? */
126
127 /*
128 * Number of items in the locks cache, array, and hash table respectively.
129 * (These are packed together to avoid padding in the struct.)
130 */
131 uint8 nlocks; /* number of owned locks */
132 uint8 narr; /* how many items are stored in the array */
133 uint32 nhash; /* how many items are stored in the hash */
134
135 /*
136 * The fixed-size array for recent resources.
137 *
138 * If 'sorted' is set, the contents are sorted by release priority.
139 */
141
142 /*
143 * The hash table. Uses open-addressing. 'nhash' is the number of items
144 * present; if it would exceed 'grow_at', we enlarge it and re-hash.
145 * 'grow_at' should be rather less than 'capacity' so that we don't waste
146 * too much time searching for empty slots.
147 *
148 * If 'sorted' is set, the contents are no longer hashed, but sorted by
149 * release priority. The first 'nhash' elements are occupied, the rest
150 * are empty.
151 */
153 uint32 capacity; /* allocated length of hash[] */
154 uint32 grow_at; /* grow hash when reach this */
155
156 /* The local locks cache. */
157 LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */
158};
159
160
161/*****************************************************************************
162 * GLOBAL MEMORY *
163 *****************************************************************************/
164
169
170/* #define RESOWNER_STATS */
171
172#ifdef RESOWNER_STATS
173static int narray_lookups = 0;
174static int nhash_lookups = 0;
175#endif
176
177/*
178 * List of add-on callbacks for resource releasing
179 */
181{
184 void *arg;
186
188
189
190/* Internal routines */
191static inline uint32 hash_resource_elem(Datum value, const ResourceOwnerDesc *kind);
193 const ResourceOwnerDesc *kind);
194static int resource_priority_cmp(const void *a, const void *b);
195static void ResourceOwnerSort(ResourceOwner owner);
196static void ResourceOwnerReleaseAll(ResourceOwner owner,
198 bool printLeakWarnings);
201 bool isCommit,
202 bool isTopLevel);
203static void ReleaseAuxProcessResourcesCallback(int code, Datum arg);
204
205
206/*****************************************************************************
207 * INTERNAL ROUTINES *
208 *****************************************************************************/
209
210/*
211 * Hash function for value+kind combination.
212 */
213static inline uint32
215{
216 /*
217 * Most resource kinds store a pointer in 'value', and pointers are unique
218 * all on their own. But some resources store plain integers (Files and
219 * Buffers as of this writing), so we want to incorporate the 'kind' in
220 * the hash too, otherwise those resources will collide a lot. But
221 * because there are only a few resource kinds like that - and only a few
222 * resource kinds to begin with - we don't need to work too hard to mix
223 * 'kind' into the hash. Just add it with hash_combine(), it perturbs the
224 * result enough for our purposes.
225 */
226#if SIZEOF_DATUM == 8
227 return hash_combine64(murmurhash64((uint64) value), (uint64) kind);
228#else
229 return hash_combine(murmurhash32((uint32) value), (uint32) kind);
230#endif
231}
232
233/*
234 * Adds 'value' of given 'kind' to the ResourceOwner's hash table
235 */
236static void
238{
239 uint32 mask = owner->capacity - 1;
240 uint32 idx;
241
242 Assert(kind != NULL);
243
244 /* Insert into first free slot at or after hash location. */
245 idx = hash_resource_elem(value, kind) & mask;
246 for (;;)
247 {
248 if (owner->hash[idx].kind == NULL)
249 break; /* found a free slot */
250 idx = (idx + 1) & mask;
251 }
252 owner->hash[idx].item = value;
253 owner->hash[idx].kind = kind;
254 owner->nhash++;
255}
256
257/*
258 * Comparison function to sort by release phase and priority
259 */
260static int
261resource_priority_cmp(const void *a, const void *b)
262{
263 const ResourceElem *ra = (const ResourceElem *) a;
264 const ResourceElem *rb = (const ResourceElem *) b;
265
266 /* Note: reverse order */
267 if (ra->kind->release_phase == rb->kind->release_phase)
269 else if (ra->kind->release_phase > rb->kind->release_phase)
270 return -1;
271 else
272 return 1;
273}
274
275/*
276 * Sort resources in reverse release priority.
277 *
278 * If the hash table is in use, all the elements from the fixed-size array are
279 * moved to the hash table, and then the hash table is sorted. If there is no
280 * hash table, then the fixed-size array is sorted directly. In either case,
281 * the result is one sorted array that contains all the resources.
282 */
283static void
285{
288
289 if (owner->nhash == 0)
290 {
291 items = owner->arr;
292 nitems = owner->narr;
293 }
294 else
295 {
296 /*
297 * Compact the hash table, so that all the elements are in the
298 * beginning of the 'hash' array, with no empty elements.
299 */
300 uint32 dst = 0;
301
302 for (int idx = 0; idx < owner->capacity; idx++)
303 {
304 if (owner->hash[idx].kind != NULL)
305 {
306 if (dst != idx)
307 owner->hash[dst] = owner->hash[idx];
308 dst++;
309 }
310 }
311
312 /*
313 * Move all entries from the fixed-size array to 'hash'.
314 *
315 * RESOWNER_HASH_MAX_ITEMS is defined so that there is always enough
316 * free space to move all the elements from the fixed-size array to
317 * the hash.
318 */
319 Assert(dst + owner->narr <= owner->capacity);
320 for (int idx = 0; idx < owner->narr; idx++)
321 {
322 owner->hash[dst] = owner->arr[idx];
323 dst++;
324 }
325 Assert(dst == owner->nhash + owner->narr);
326 owner->narr = 0;
327 owner->nhash = dst;
328
329 items = owner->hash;
330 nitems = owner->nhash;
331 }
332
334}
335
336/*
337 * Call the ReleaseResource callback on entries with given 'phase'.
338 */
339static void
341 bool printLeakWarnings)
342{
345
346 /*
347 * ResourceOwnerSort must've been called already. All the resources are
348 * either in the array or the hash.
349 */
350 Assert(owner->releasing);
351 Assert(owner->sorted);
352 if (owner->nhash == 0)
353 {
354 items = owner->arr;
355 nitems = owner->narr;
356 }
357 else
358 {
359 Assert(owner->narr == 0);
360 items = owner->hash;
361 nitems = owner->nhash;
362 }
363
364 /*
365 * The resources are sorted in reverse priority order. Release them
366 * starting from the end, until we hit the end of the phase that we are
367 * releasing now. We will continue from there when called again for the
368 * next phase.
369 */
370 while (nitems > 0)
371 {
372 uint32 idx = nitems - 1;
373 Datum value = items[idx].item;
374 const ResourceOwnerDesc *kind = items[idx].kind;
375
376 if (kind->release_phase > phase)
377 break;
378 Assert(kind->release_phase == phase);
379
380 if (printLeakWarnings)
381 {
382 char *res_str;
383
384 res_str = kind->DebugPrint ?
385 kind->DebugPrint(value)
386 : psprintf("%s %p", kind->name, DatumGetPointer(value));
387 elog(WARNING, "resource was not closed: %s", res_str);
388 pfree(res_str);
389 }
390 kind->ReleaseResource(value);
391 nitems--;
392 }
393 if (owner->nhash == 0)
394 owner->narr = nitems;
395 else
396 owner->nhash = nitems;
397}
398
399
400/*****************************************************************************
401 * EXPORTED ROUTINES *
402 *****************************************************************************/
403
404
405/*
406 * ResourceOwnerCreate
407 * Create an empty ResourceOwner.
408 *
409 * All ResourceOwner objects are kept in TopMemoryContext, since they should
410 * only be freed explicitly.
411 */
414{
415 ResourceOwner owner;
416
418 sizeof(struct ResourceOwnerData));
419 owner->name = name;
420
421 if (parent)
422 {
423 owner->parent = parent;
424 owner->nextchild = parent->firstchild;
425 parent->firstchild = owner;
426 }
427
428 return owner;
429}
430
431/*
432 * Make sure there is room for at least one more resource in an array.
433 *
434 * This is separate from actually inserting a resource because if we run out
435 * of memory, it's critical to do so *before* acquiring the resource.
436 *
437 * NB: Make sure there are no unrelated ResourceOwnerRemember() calls between
438 * your ResourceOwnerEnlarge() call and the ResourceOwnerRemember() call that
439 * you reserved the space for!
440 */
441void
443{
444 /*
445 * Mustn't try to remember more resources after we have already started
446 * releasing
447 */
448 if (owner->releasing)
449 elog(ERROR, "ResourceOwnerEnlarge called after release started");
450
451 if (owner->narr < RESOWNER_ARRAY_SIZE)
452 return; /* no work needed */
453
454 /*
455 * Is there space in the hash? If not, enlarge it.
456 */
457 if (owner->narr + owner->nhash >= owner->grow_at)
458 {
459 uint32 i,
460 oldcap,
461 newcap;
462 ResourceElem *oldhash;
463 ResourceElem *newhash;
464
465 oldhash = owner->hash;
466 oldcap = owner->capacity;
467
468 /* Double the capacity (it must stay a power of 2!) */
469 newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE;
471 newcap * sizeof(ResourceElem));
472
473 /*
474 * We assume we can't fail below this point, so OK to scribble on the
475 * owner
476 */
477 owner->hash = newhash;
478 owner->capacity = newcap;
479 owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap);
480 owner->nhash = 0;
481
482 if (oldhash != NULL)
483 {
484 /*
485 * Transfer any pre-existing entries into the new hash table; they
486 * don't necessarily go where they were before, so this simple
487 * logic is the best way.
488 */
489 for (i = 0; i < oldcap; i++)
490 {
491 if (oldhash[i].kind != NULL)
492 ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
493 }
494
495 /* And release old hash table. */
496 pfree(oldhash);
497 }
498 }
499
500 /* Move items from the array to the hash */
501 for (int i = 0; i < owner->narr; i++)
502 ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
503 owner->narr = 0;
504
505 Assert(owner->nhash <= owner->grow_at);
506}
507
508/*
509 * Remember that an object is owned by a ResourceOwner
510 *
511 * Caller must have previously done ResourceOwnerEnlarge()
512 */
513void
515{
516 uint32 idx;
517
518 /* sanity check the ResourceOwnerDesc */
519 Assert(kind->release_phase != 0);
520 Assert(kind->release_priority != 0);
521
522 /*
523 * Mustn't try to remember more resources after we have already started
524 * releasing. We already checked this in ResourceOwnerEnlarge.
525 */
526 Assert(!owner->releasing);
527 Assert(!owner->sorted);
528
529 if (owner->narr >= RESOWNER_ARRAY_SIZE)
530 {
531 /* forgot to call ResourceOwnerEnlarge? */
532 elog(ERROR, "ResourceOwnerRemember called but array was full");
533 }
534
535 /* Append to the array. */
536 idx = owner->narr;
537 owner->arr[idx].item = value;
538 owner->arr[idx].kind = kind;
539 owner->narr++;
540}
541
542/*
543 * Forget that an object is owned by a ResourceOwner
544 *
545 * Note: If same resource ID is associated with the ResourceOwner more than
546 * once, one instance is removed.
547 *
548 * Note: Forgetting a resource does not guarantee that there is room to
549 * remember a new resource. One exception is when you forget the most
550 * recently remembered resource; that does make room for a new remember call.
551 * Some code callers rely on that exception.
552 */
553void
555{
556 /*
557 * Mustn't call this after we have already started releasing resources.
558 * (Release callback functions are not allowed to release additional
559 * resources.)
560 */
561 if (owner->releasing)
562 elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
563 Assert(!owner->sorted);
564
565 /* Search through all items in the array first. */
566 for (int i = owner->narr - 1; i >= 0; i--)
567 {
568 if (owner->arr[i].item == value &&
569 owner->arr[i].kind == kind)
570 {
571 owner->arr[i] = owner->arr[owner->narr - 1];
572 owner->narr--;
573
574#ifdef RESOWNER_STATS
575 narray_lookups++;
576#endif
577 return;
578 }
579 }
580
581 /* Search hash */
582 if (owner->nhash > 0)
583 {
584 uint32 mask = owner->capacity - 1;
585 uint32 idx;
586
587 idx = hash_resource_elem(value, kind) & mask;
588 for (uint32 i = 0; i < owner->capacity; i++)
589 {
590 if (owner->hash[idx].item == value &&
591 owner->hash[idx].kind == kind)
592 {
593 owner->hash[idx].item = (Datum) 0;
594 owner->hash[idx].kind = NULL;
595 owner->nhash--;
596
597#ifdef RESOWNER_STATS
598 nhash_lookups++;
599#endif
600 return;
601 }
602 idx = (idx + 1) & mask;
603 }
604 }
605
606 /*
607 * Use %p to print the reference, since most objects tracked by a resource
608 * owner are pointers. It's a bit misleading if it's not a pointer, but
609 * this is a programmer error, anyway.
610 */
611 elog(ERROR, "%s %p is not owned by resource owner %s",
612 kind->name, DatumGetPointer(value), owner->name);
613}
614
615/*
616 * ResourceOwnerRelease
617 * Release all resources owned by a ResourceOwner and its descendants,
618 * but don't delete the owner objects themselves.
619 *
620 * Note that this executes just one phase of release, and so typically
621 * must be called three times. We do it this way because (a) we want to
622 * do all the recursion separately for each phase, thereby preserving
623 * the needed order of operations; and (b) xact.c may have other operations
624 * to do between the phases.
625 *
626 * phase: release phase to execute
627 * isCommit: true for successful completion of a query or transaction,
628 * false for unsuccessful
629 * isTopLevel: true if completing a main transaction, else false
630 *
631 * isCommit is passed because some modules may expect that their resources
632 * were all released already if the transaction or portal finished normally.
633 * If so it is reasonable to give a warning (NOT an error) should any
634 * unreleased resources be present. When isCommit is false, such warnings
635 * are generally inappropriate.
636 *
637 * isTopLevel is passed when we are releasing TopTransactionResourceOwner
638 * at completion of a main transaction. This generally means that *all*
639 * resources will be released, and so we can optimize things a bit.
640 *
641 * NOTE: After starting the release process, by calling this function, no new
642 * resources can be remembered in the resource owner. You also cannot call
643 * ResourceOwnerForget on any previously remembered resources to release
644 * resources "in retail" after that, you must let the bulk release take care
645 * of them.
646 */
647void
650 bool isCommit,
651 bool isTopLevel)
652{
653 /* There's not currently any setup needed before recursing */
654 ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
655
656#ifdef RESOWNER_STATS
657 if (isTopLevel)
658 {
659 elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d",
660 narray_lookups, nhash_lookups);
661 narray_lookups = 0;
662 nhash_lookups = 0;
663 }
664#endif
665}
666
667static void
670 bool isCommit,
671 bool isTopLevel)
672{
673 ResourceOwner child;
674 ResourceOwner save;
677
678 /* Recurse to handle descendants */
679 for (child = owner->firstchild; child != NULL; child = child->nextchild)
680 ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
681
682 /*
683 * To release the resources in the right order, sort them by phase and
684 * priority.
685 *
686 * The ReleaseResource callback functions are not allowed to remember or
687 * forget any other resources after this. Otherwise we lose track of where
688 * we are in processing the hash/array.
689 */
690 if (!owner->releasing)
691 {
693 Assert(!owner->sorted);
694 owner->releasing = true;
695 }
696 else
697 {
698 /*
699 * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not
700 * the first call to ResourceOwnerRelease. But if an error happens
701 * between the release phases, we might get called again for the same
702 * ResourceOwner from AbortTransaction.
703 */
704 }
705 if (!owner->sorted)
706 {
707 ResourceOwnerSort(owner);
708 owner->sorted = true;
709 }
710
711 /*
712 * Make CurrentResourceOwner point to me, so that the release callback
713 * functions know which resource owner is been released.
714 */
716 CurrentResourceOwner = owner;
717
719 {
720 /*
721 * Release all resources that need to be released before the locks.
722 *
723 * During a commit, there shouldn't be any remaining resources ---
724 * that would indicate failure to clean up the executor correctly ---
725 * so issue warnings. In the abort case, just clean up quietly.
726 */
727 ResourceOwnerReleaseAll(owner, phase, isCommit);
728 }
729 else if (phase == RESOURCE_RELEASE_LOCKS)
730 {
731 if (isTopLevel)
732 {
733 /*
734 * For a top-level xact we are going to release all locks (or at
735 * least all non-session locks), so just do a single lmgr call at
736 * the top of the recursion.
737 */
738 if (owner == TopTransactionResourceOwner)
739 {
740 ProcReleaseLocks(isCommit);
741 ReleasePredicateLocks(isCommit, false);
742 }
743 }
744 else
745 {
746 /*
747 * Release locks retail. Note that if we are committing a
748 * subtransaction, we do NOT release its locks yet, but transfer
749 * them to the parent.
750 */
751 LOCALLOCK **locks;
752 int nlocks;
753
754 Assert(owner->parent != NULL);
755
756 /*
757 * Pass the list of locks owned by this resource owner to the lock
758 * manager, unless it has overflowed.
759 */
760 if (owner->nlocks > MAX_RESOWNER_LOCKS)
761 {
762 locks = NULL;
763 nlocks = 0;
764 }
765 else
766 {
767 locks = owner->locks;
768 nlocks = owner->nlocks;
769 }
770
771 if (isCommit)
772 LockReassignCurrentOwner(locks, nlocks);
773 else
774 LockReleaseCurrentOwner(locks, nlocks);
775 }
776 }
777 else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
778 {
779 /*
780 * Release all resources that need to be released after the locks.
781 */
782 ResourceOwnerReleaseAll(owner, phase, isCommit);
783 }
784
785 /* Let add-on modules get a chance too */
786 for (item = ResourceRelease_callbacks; item; item = next)
787 {
788 /* allow callbacks to unregister themselves when called */
789 next = item->next;
790 item->callback(phase, isCommit, isTopLevel, item->arg);
791 }
792
794}
795
796/*
797 * ResourceOwnerReleaseAllOfKind
798 * Release all resources of a certain type held by this owner.
799 */
800void
802{
803 /* Mustn't call this after we have already started releasing resources. */
804 if (owner->releasing)
805 elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
806 Assert(!owner->sorted);
807
808 /*
809 * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember
810 * while we're scanning the owner. Enlarging the hash would cause us to
811 * lose track of the point we're scanning.
812 */
813 owner->releasing = true;
814
815 /* Array first */
816 for (int i = 0; i < owner->narr; i++)
817 {
818 if (owner->arr[i].kind == kind)
819 {
820 Datum value = owner->arr[i].item;
821
822 owner->arr[i] = owner->arr[owner->narr - 1];
823 owner->narr--;
824 i--;
825
826 kind->ReleaseResource(value);
827 }
828 }
829
830 /* Then hash */
831 for (int i = 0; i < owner->capacity; i++)
832 {
833 if (owner->hash[i].kind == kind)
834 {
835 Datum value = owner->hash[i].item;
836
837 owner->hash[i].item = (Datum) 0;
838 owner->hash[i].kind = NULL;
839 owner->nhash--;
840
841 kind->ReleaseResource(value);
842 }
843 }
844 owner->releasing = false;
845}
846
847/*
848 * ResourceOwnerDelete
849 * Delete an owner object and its descendants.
850 *
851 * The caller must have already released all resources in the object tree.
852 */
853void
855{
856 /* We had better not be deleting CurrentResourceOwner ... */
858
859 /* And it better not own any resources, either */
860 Assert(owner->narr == 0);
861 Assert(owner->nhash == 0);
862 Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
863
864 /*
865 * Delete children. The recursive call will delink the child from me, so
866 * just iterate as long as there is a child.
867 */
868 while (owner->firstchild != NULL)
870
871 /*
872 * We delink the owner from its parent before deleting it, so that if
873 * there's an error we won't have deleted/busted owners still attached to
874 * the owner tree. Better a leak than a crash.
875 */
876 ResourceOwnerNewParent(owner, NULL);
877
878 /* And free the object. */
879 if (owner->hash)
880 pfree(owner->hash);
881 pfree(owner);
882}
883
884/*
885 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
886 */
889{
890 return owner->parent;
891}
892
893/*
894 * Reassign a ResourceOwner to have a new parent
895 */
896void
898 ResourceOwner newparent)
899{
900 ResourceOwner oldparent = owner->parent;
901
902 if (oldparent)
903 {
904 if (owner == oldparent->firstchild)
905 oldparent->firstchild = owner->nextchild;
906 else
907 {
908 ResourceOwner child;
909
910 for (child = oldparent->firstchild; child; child = child->nextchild)
911 {
912 if (owner == child->nextchild)
913 {
914 child->nextchild = owner->nextchild;
915 break;
916 }
917 }
918 }
919 }
920
921 if (newparent)
922 {
923 Assert(owner != newparent);
924 owner->parent = newparent;
925 owner->nextchild = newparent->firstchild;
926 newparent->firstchild = owner;
927 }
928 else
929 {
930 owner->parent = NULL;
931 owner->nextchild = NULL;
932 }
933}
934
935/*
936 * Register or deregister callback functions for resource cleanup
937 *
938 * These functions can be used by dynamically loaded modules. These used
939 * to be the only way for an extension to register custom resource types
940 * with a resource owner, but nowadays it is easier to define a new
941 * ResourceOwnerDesc with custom callbacks.
942 */
943void
945{
947
951 item->callback = callback;
952 item->arg = arg;
955}
956
957void
959{
962
963 prev = NULL;
964 for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
965 {
966 if (item->callback == callback && item->arg == arg)
967 {
968 if (prev)
969 prev->next = item->next;
970 else
972 pfree(item);
973 break;
974 }
975 }
976}
977
978/*
979 * Establish an AuxProcessResourceOwner for the current process.
980 */
981void
983{
986 AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
988
989 /*
990 * Register a shmem-exit callback for cleanup of aux-process resource
991 * owner. (This needs to run after, e.g., ShutdownXLOG.)
992 */
994}
995
996/*
997 * Convenience routine to release all resources tracked in
998 * AuxProcessResourceOwner (but that resowner is not destroyed here).
999 * Warn about leaked resources if isCommit is true.
1000 */
1001void
1003{
1004 /*
1005 * At this writing, the only thing that could actually get released is
1006 * buffer pins; but we may as well do the full release protocol.
1007 */
1010 isCommit, true);
1013 isCommit, true);
1016 isCommit, true);
1017 /* allow it to be reused */
1020}
1021
1022/*
1023 * Shmem-exit callback for the same.
1024 * Warn about leaked resources if process exit code is zero (ie normal).
1025 */
1026static void
1028{
1029 bool isCommit = (code == 0);
1030
1032}
1033
1034/*
1035 * Remember that a Local Lock is owned by a ResourceOwner
1036 *
1037 * This is different from the generic ResourceOwnerRemember in that the list of
1038 * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
1039 * and when it overflows, we stop tracking locks. The point of only remembering
1040 * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
1041 * ResourceOwnerForgetLock doesn't need to scan through a large array to find
1042 * the entry.
1043 */
1044void
1046{
1047 Assert(locallock != NULL);
1048
1049 if (owner->nlocks > MAX_RESOWNER_LOCKS)
1050 return; /* we have already overflowed */
1051
1052 if (owner->nlocks < MAX_RESOWNER_LOCKS)
1053 owner->locks[owner->nlocks] = locallock;
1054 else
1055 {
1056 /* overflowed */
1057 }
1058 owner->nlocks++;
1059}
1060
1061/*
1062 * Forget that a Local Lock is owned by a ResourceOwner
1063 */
1064void
1066{
1067 int i;
1068
1069 if (owner->nlocks > MAX_RESOWNER_LOCKS)
1070 return; /* we have overflowed */
1071
1072 Assert(owner->nlocks > 0);
1073 for (i = owner->nlocks - 1; i >= 0; i--)
1074 {
1075 if (locallock == owner->locks[i])
1076 {
1077 owner->locks[i] = owner->locks[owner->nlocks - 1];
1078 owner->nlocks--;
1079 return;
1080 }
1081 }
1082 elog(ERROR, "lock reference %p is not owned by resource owner %s",
1083 locallock, owner->name);
1084}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
static int32 next
Definition: blutils.c:221
uint8_t uint8
Definition: c.h:486
#define Assert(condition)
Definition: c.h:815
uint64_t uint64
Definition: c.h:489
uint32_t uint32
Definition: c.h:488
#define LOG
Definition: elog.h:31
#define WARNING
Definition: elog.h:36
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
static uint64 hash_combine64(uint64 a, uint64 b)
Definition: hashfn.h:80
static uint64 murmurhash64(uint64 data)
Definition: hashfn.h:106
static uint32 hash_combine(uint32 a, uint32 b)
Definition: hashfn.h:68
static uint32 murmurhash32(uint32 data)
Definition: hashfn.h:92
#define nitems(x)
Definition: indent.h:31
static struct @162 value
static int pg_cmp_u32(uint32 a, uint32 b)
Definition: int.h:652
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:365
int b
Definition: isn.c:69
int a
Definition: isn.c:68
int i
Definition: isn.c:72
void LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks)
Definition: lock.c:2615
void LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks)
Definition: lock.c:2520
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215
void pfree(void *pointer)
Definition: mcxt.c:1521
MemoryContext TopMemoryContext
Definition: mcxt.c:149
void * arg
#define qsort(a, b, c, d)
Definition: port.h:475
uintptr_t Datum
Definition: postgres.h:69
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
void ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe)
Definition: predicate.c:3302
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
void ResourceOwnerNewParent(ResourceOwner owner, ResourceOwner newparent)
Definition: resowner.c:897
#define MAX_RESOWNER_LOCKS
Definition: resowner.c:105
void ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
Definition: resowner.c:1045
static void ReleaseAuxProcessResourcesCallback(int code, Datum arg)
Definition: resowner.c:1027
ResourceOwner TopTransactionResourceOwner
Definition: resowner.c:167
#define RESOWNER_HASH_INIT_SIZE
Definition: resowner.c:77
static void ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase, bool printLeakWarnings)
Definition: resowner.c:340
void UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
Definition: resowner.c:958
ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char *name)
Definition: resowner.c:413
#define RESOWNER_HASH_MAX_ITEMS(capacity)
Definition: resowner.c:89
void ReleaseAuxProcessResources(bool isCommit)
Definition: resowner.c:1002
ResourceOwner ResourceOwnerGetParent(ResourceOwner owner)
Definition: resowner.c:888
ResourceOwner CurrentResourceOwner
Definition: resowner.c:165
static uint32 hash_resource_elem(Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:214
struct ResourceElem ResourceElem
static ResourceReleaseCallbackItem * ResourceRelease_callbacks
Definition: resowner.c:187
static void ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:237
void CreateAuxProcessResourceOwner(void)
Definition: resowner.c:982
void ResourceOwnerRelease(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
Definition: resowner.c:648
static int resource_priority_cmp(const void *a, const void *b)
Definition: resowner.c:261
void RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
Definition: resowner.c:944
static void ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
Definition: resowner.c:668
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:554
void ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
Definition: resowner.c:801
ResourceOwner AuxProcessResourceOwner
Definition: resowner.c:168
struct ResourceReleaseCallbackItem ResourceReleaseCallbackItem
void ResourceOwnerDelete(ResourceOwner owner)
Definition: resowner.c:854
ResourceOwner CurTransactionResourceOwner
Definition: resowner.c:166
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:514
#define RESOWNER_ARRAY_SIZE
Definition: resowner.c:71
static void ResourceOwnerSort(ResourceOwner owner)
Definition: resowner.c:284
StaticAssertDecl(RESOWNER_HASH_MAX_ITEMS(RESOWNER_HASH_INIT_SIZE) >=RESOWNER_ARRAY_SIZE, "initial hash size too small compared to array size")
void ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
Definition: resowner.c:1065
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition: resowner.c:442
ResourceReleasePhase
Definition: resowner.h:53
@ RESOURCE_RELEASE_LOCKS
Definition: resowner.h:55
@ RESOURCE_RELEASE_BEFORE_LOCKS
Definition: resowner.h:54
@ RESOURCE_RELEASE_AFTER_LOCKS
Definition: resowner.h:56
struct ResourceOwnerData * ResourceOwner
Definition: resowner.h:27
void(* ResourceReleaseCallback)(ResourceReleasePhase phase, bool isCommit, bool isTopLevel, void *arg)
Definition: resowner.h:126
void ProcReleaseLocks(bool isCommit)
Definition: proc.c:840
Datum item
Definition: resowner.c:64
const ResourceOwnerDesc * kind
Definition: resowner.c:65
ResourceOwner parent
Definition: resowner.c:112
const char * name
Definition: resowner.c:115
LOCALLOCK * locks[MAX_RESOWNER_LOCKS]
Definition: resowner.c:157
ResourceOwner nextchild
Definition: resowner.c:114
ResourceOwner firstchild
Definition: resowner.c:113
ResourceElem * hash
Definition: resowner.c:152
ResourceElem arr[RESOWNER_ARRAY_SIZE]
Definition: resowner.c:140
char *(* DebugPrint)(Datum res)
Definition: resowner.h:118
ResourceReleasePhase release_phase
Definition: resowner.h:96
void(* ReleaseResource)(Datum res)
Definition: resowner.h:108
ResourceReleasePriority release_priority
Definition: resowner.h:97
const char * name
Definition: resowner.h:93
struct ResourceReleaseCallbackItem * next
Definition: resowner.c:182
ResourceReleaseCallback callback
Definition: resowner.c:183
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:46
static ItemArray items
Definition: test_tidstore.c:48
const char * name