PostgreSQL Source Code git master
Loading...
Searching...
No Matches
resowner.c File Reference
#include "postgres.h"
#include "common/hashfn.h"
#include "common/int.h"
#include "lib/ilist.h"
#include "storage/aio.h"
#include "storage/ipc.h"
#include "storage/predicate.h"
#include "storage/proc.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
Include dependency graph for resowner.c:

Go to the source code of this file.

Data Structures

struct  ResourceElem
 
struct  ResourceOwnerData
 
struct  ResourceReleaseCallbackItem
 

Macros

#define RESOWNER_ARRAY_SIZE   32
 
#define RESOWNER_HASH_INIT_SIZE   64
 
#define RESOWNER_HASH_MAX_ITEMS(capacity)    Min(capacity - RESOWNER_ARRAY_SIZE, (capacity)/4 * 3)
 
#define MAX_RESOWNER_LOCKS   15
 

Typedefs

typedef struct ResourceElem ResourceElem
 
typedef struct ResourceReleaseCallbackItem ResourceReleaseCallbackItem
 

Functions

 StaticAssertDecl (RESOWNER_HASH_MAX_ITEMS(RESOWNER_HASH_INIT_SIZE) >=RESOWNER_ARRAY_SIZE, "initial hash size too small compared to array size")
 
static uint32 hash_resource_elem (Datum value, const ResourceOwnerDesc *kind)
 
static void ResourceOwnerAddToHash (ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
 
static int resource_priority_cmp (const void *a, const void *b)
 
static void ResourceOwnerSort (ResourceOwner owner)
 
static void ResourceOwnerReleaseAll (ResourceOwner owner, ResourceReleasePhase phase, bool printLeakWarnings)
 
static void ResourceOwnerReleaseInternal (ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
 
static void ReleaseAuxProcessResourcesCallback (int code, Datum arg)
 
ResourceOwner ResourceOwnerCreate (ResourceOwner parent, const char *name)
 
void ResourceOwnerEnlarge (ResourceOwner owner)
 
void ResourceOwnerRemember (ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
 
void ResourceOwnerForget (ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
 
void ResourceOwnerRelease (ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
 
void ResourceOwnerReleaseAllOfKind (ResourceOwner owner, const ResourceOwnerDesc *kind)
 
void ResourceOwnerDelete (ResourceOwner owner)
 
ResourceOwner ResourceOwnerGetParent (ResourceOwner owner)
 
void ResourceOwnerNewParent (ResourceOwner owner, ResourceOwner newparent)
 
void RegisterResourceReleaseCallback (ResourceReleaseCallback callback, void *arg)
 
void UnregisterResourceReleaseCallback (ResourceReleaseCallback callback, void *arg)
 
void CreateAuxProcessResourceOwner (void)
 
void ReleaseAuxProcessResources (bool isCommit)
 
void ResourceOwnerRememberLock (ResourceOwner owner, LOCALLOCK *locallock)
 
void ResourceOwnerForgetLock (ResourceOwner owner, LOCALLOCK *locallock)
 
void ResourceOwnerRememberAioHandle (ResourceOwner owner, struct dlist_node *ioh_node)
 
void ResourceOwnerForgetAioHandle (ResourceOwner owner, struct dlist_node *ioh_node)
 

Variables

ResourceOwner CurrentResourceOwner = NULL
 
ResourceOwner CurTransactionResourceOwner = NULL
 
ResourceOwner TopTransactionResourceOwner = NULL
 
ResourceOwner AuxProcessResourceOwner = NULL
 
static ResourceReleaseCallbackItemResourceRelease_callbacks = NULL
 

Macro Definition Documentation

◆ MAX_RESOWNER_LOCKS

#define MAX_RESOWNER_LOCKS   15

Definition at line 107 of file resowner.c.

◆ RESOWNER_ARRAY_SIZE

#define RESOWNER_ARRAY_SIZE   32

Definition at line 73 of file resowner.c.

◆ RESOWNER_HASH_INIT_SIZE

#define RESOWNER_HASH_INIT_SIZE   64

Definition at line 79 of file resowner.c.

◆ RESOWNER_HASH_MAX_ITEMS

#define RESOWNER_HASH_MAX_ITEMS (   capacity)     Min(capacity - RESOWNER_ARRAY_SIZE, (capacity)/4 * 3)

Definition at line 91 of file resowner.c.

112{
113 ResourceOwner parent; /* NULL if no parent (toplevel owner) */
114 ResourceOwner firstchild; /* head of linked list of children */
115 ResourceOwner nextchild; /* next child of same parent */
116 const char *name; /* name (just for debugging) */
117
118 /*
119 * When ResourceOwnerRelease is called, we sort the 'hash' and 'arr' by
120 * the release priority. After that, no new resources can be remembered
121 * or forgotten in retail. We have separate flags because
122 * ResourceOwnerReleaseAllOfKind() temporarily sets 'releasing' without
123 * sorting the arrays.
124 */
125 bool releasing;
126 bool sorted; /* are 'hash' and 'arr' sorted by priority? */
127
128 /*
129 * Number of items in the locks cache, array, and hash table respectively.
130 * (These are packed together to avoid padding in the struct.)
131 */
132 uint8 nlocks; /* number of owned locks */
133 uint8 narr; /* how many items are stored in the array */
134 uint32 nhash; /* how many items are stored in the hash */
135
136 /*
137 * The fixed-size array for recent resources.
138 *
139 * If 'sorted' is set, the contents are sorted by release priority.
140 */
142
143 /*
144 * The hash table. Uses open-addressing. 'nhash' is the number of items
145 * present; if it would exceed 'grow_at', we enlarge it and re-hash.
146 * 'grow_at' should be rather less than 'capacity' so that we don't waste
147 * too much time searching for empty slots.
148 *
149 * If 'sorted' is set, the contents are no longer hashed, but sorted by
150 * release priority. The first 'nhash' elements are occupied, the rest
151 * are empty.
152 */
154 uint32 capacity; /* allocated length of hash[] */
155 uint32 grow_at; /* grow hash when reach this */
156
157 /* The local locks cache. */
158 LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */
159
160 /*
161 * AIO handles need be registered in critical sections and therefore
162 * cannot use the normal ResourceElem mechanism.
163 */
164 dlist_head aio_handles;
165};
166
167
168/*****************************************************************************
169 * GLOBAL MEMORY *
170 *****************************************************************************/
171
176
177/* #define RESOWNER_STATS */
178
179#ifdef RESOWNER_STATS
180static int narray_lookups = 0;
181static int nhash_lookups = 0;
182#endif
183
184/*
185 * List of add-on callbacks for resource releasing
186 */
187typedef struct ResourceReleaseCallbackItem
188{
191 void *arg;
193
195
196
197/* Internal routines */
198static inline uint32 hash_resource_elem(Datum value, const ResourceOwnerDesc *kind);
200 const ResourceOwnerDesc *kind);
201static int resource_priority_cmp(const void *a, const void *b);
202static void ResourceOwnerSort(ResourceOwner owner);
203static void ResourceOwnerReleaseAll(ResourceOwner owner,
205 bool printLeakWarnings);
208 bool isCommit,
209 bool isTopLevel);
210static void ReleaseAuxProcessResourcesCallback(int code, Datum arg);
211
212
213/*****************************************************************************
214 * INTERNAL ROUTINES *
215 *****************************************************************************/
216
217/*
218 * Hash function for value+kind combination.
219 */
220static inline uint32
222{
223 /*
224 * Most resource kinds store a pointer in 'value', and pointers are unique
225 * all on their own. But some resources store plain integers (Files and
226 * Buffers as of this writing), so we want to incorporate the 'kind' in
227 * the hash too, otherwise those resources will collide a lot. But
228 * because there are only a few resource kinds like that - and only a few
229 * resource kinds to begin with - we don't need to work too hard to mix
230 * 'kind' into the hash. Just add it with hash_combine(), it perturbs the
231 * result enough for our purposes.
232 */
234 (uint64) (uintptr_t) kind);
235}
236
237/*
238 * Adds 'value' of given 'kind' to the ResourceOwner's hash table
239 */
240static void
242{
243 uint32 mask = owner->capacity - 1;
244 uint32 idx;
245
246 Assert(kind != NULL);
247
248 /* Insert into first free slot at or after hash location. */
249 idx = hash_resource_elem(value, kind) & mask;
250 for (;;)
251 {
252 if (owner->hash[idx].kind == NULL)
253 break; /* found a free slot */
254 idx = (idx + 1) & mask;
255 }
256 owner->hash[idx].item = value;
257 owner->hash[idx].kind = kind;
258 owner->nhash++;
259}
260
261/*
262 * Comparison function to sort by release phase and priority
263 */
264static int
265resource_priority_cmp(const void *a, const void *b)
266{
267 const ResourceElem *ra = (const ResourceElem *) a;
268 const ResourceElem *rb = (const ResourceElem *) b;
269
270 /* Note: reverse order */
271 if (ra->kind->release_phase == rb->kind->release_phase)
272 return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority);
273 else if (ra->kind->release_phase > rb->kind->release_phase)
274 return -1;
275 else
276 return 1;
277}
278
279/*
280 * Sort resources in reverse release priority.
281 *
282 * If the hash table is in use, all the elements from the fixed-size array are
283 * moved to the hash table, and then the hash table is sorted. If there is no
284 * hash table, then the fixed-size array is sorted directly. In either case,
285 * the result is one sorted array that contains all the resources.
286 */
287static void
289{
292
293 if (owner->nhash == 0)
294 {
295 items = owner->arr;
296 nitems = owner->narr;
297 }
298 else
299 {
300 /*
301 * Compact the hash table, so that all the elements are in the
302 * beginning of the 'hash' array, with no empty elements.
303 */
304 uint32 dst = 0;
305
306 for (int idx = 0; idx < owner->capacity; idx++)
307 {
308 if (owner->hash[idx].kind != NULL)
309 {
310 if (dst != idx)
311 owner->hash[dst] = owner->hash[idx];
312 dst++;
313 }
314 }
315
316 /*
317 * Move all entries from the fixed-size array to 'hash'.
318 *
319 * RESOWNER_HASH_MAX_ITEMS is defined so that there is always enough
320 * free space to move all the elements from the fixed-size array to
321 * the hash.
322 */
323 Assert(dst + owner->narr <= owner->capacity);
324 for (int idx = 0; idx < owner->narr; idx++)
325 {
326 owner->hash[dst] = owner->arr[idx];
327 dst++;
328 }
329 Assert(dst == owner->nhash + owner->narr);
330 owner->narr = 0;
331 owner->nhash = dst;
332
333 items = owner->hash;
334 nitems = owner->nhash;
335 }
336
338}
339
340/*
341 * Call the ReleaseResource callback on entries with given 'phase'.
342 */
343static void
346{
349 bool using_arr;
350
351 /*
352 * ResourceOwnerSort must've been called already. All the resources are
353 * either in the array or the hash.
354 */
355 Assert(owner->releasing);
356 Assert(owner->sorted);
357 if (owner->nhash == 0)
358 {
359 items = owner->arr;
360 nitems = owner->narr;
361 using_arr = true;
362 }
363 else
364 {
365 Assert(owner->narr == 0);
366 items = owner->hash;
367 nitems = owner->nhash;
368 using_arr = false;
369 }
370
371 /*
372 * The resources are sorted in reverse priority order. Release them
373 * starting from the end, until we hit the end of the phase that we are
374 * releasing now. We will continue from there when called again for the
375 * next phase.
376 */
377 while (nitems > 0)
378 {
379 uint32 idx = nitems - 1;
380 Datum value = items[idx].item;
381 const ResourceOwnerDesc *kind = items[idx].kind;
382
383 if (kind->release_phase > phase)
384 break;
385 Assert(kind->release_phase == phase);
386
388 {
389 char *res_str;
390
391 res_str = kind->DebugPrint ?
392 kind->DebugPrint(value)
393 : psprintf("%s %p", kind->name, DatumGetPointer(value));
394 elog(WARNING, "resource was not closed: %s", res_str);
395 pfree(res_str);
396 }
397
398 /*
399 * Update stored count to forget the item before calling its
400 * ReleaseResource method. This avoids double-free crashes in case an
401 * error gets thrown within ReleaseResource.
402 */
403 nitems--;
404 if (using_arr)
405 owner->narr = nitems;
406 else
407 owner->nhash = nitems;
408
409 kind->ReleaseResource(value);
410 }
411}
412
413
414/*****************************************************************************
415 * EXPORTED ROUTINES *
416 *****************************************************************************/
417
418
419/*
420 * ResourceOwnerCreate
421 * Create an empty ResourceOwner.
422 *
423 * All ResourceOwner objects are kept in TopMemoryContext, since they should
424 * only be freed explicitly.
425 */
427ResourceOwnerCreate(ResourceOwner parent, const char *name)
428{
429 ResourceOwner owner;
430
432 sizeof(struct ResourceOwnerData));
433 owner->name = name;
434
435 if (parent)
436 {
437 owner->parent = parent;
438 owner->nextchild = parent->firstchild;
439 parent->firstchild = owner;
440 }
441
442 dlist_init(&owner->aio_handles);
443
444 return owner;
445}
446
447/*
448 * Make sure there is room for at least one more resource in an array.
449 *
450 * This is separate from actually inserting a resource because if we run out
451 * of memory, it's critical to do so *before* acquiring the resource.
452 *
453 * NB: Make sure there are no unrelated ResourceOwnerRemember() calls between
454 * your ResourceOwnerEnlarge() call and the ResourceOwnerRemember() call that
455 * you reserved the space for!
456 */
457void
459{
460 /*
461 * Mustn't try to remember more resources after we have already started
462 * releasing
463 */
464 if (owner->releasing)
465 elog(ERROR, "ResourceOwnerEnlarge called after release started");
466
467 if (owner->narr < RESOWNER_ARRAY_SIZE)
468 return; /* no work needed */
469
470 /*
471 * Is there space in the hash? If not, enlarge it.
472 */
473 if (owner->narr + owner->nhash >= owner->grow_at)
474 {
475 uint32 i,
476 oldcap,
477 newcap;
480
481 oldhash = owner->hash;
482 oldcap = owner->capacity;
483
484 /* Double the capacity (it must stay a power of 2!) */
487 newcap * sizeof(ResourceElem));
488
489 /*
490 * We assume we can't fail below this point, so OK to scribble on the
491 * owner
492 */
493 owner->hash = newhash;
494 owner->capacity = newcap;
496 owner->nhash = 0;
497
498 if (oldhash != NULL)
499 {
500 /*
501 * Transfer any pre-existing entries into the new hash table; they
502 * don't necessarily go where they were before, so this simple
503 * logic is the best way.
504 */
505 for (i = 0; i < oldcap; i++)
506 {
507 if (oldhash[i].kind != NULL)
508 ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
509 }
510
511 /* And release old hash table. */
512 pfree(oldhash);
513 }
514 }
515
516 /* Move items from the array to the hash */
517 for (int i = 0; i < owner->narr; i++)
518 ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
519 owner->narr = 0;
520
521 Assert(owner->nhash <= owner->grow_at);
522}
523
524/*
525 * Remember that an object is owned by a ResourceOwner
526 *
527 * Caller must have previously done ResourceOwnerEnlarge()
528 */
529void
531{
532 uint32 idx;
533
534 /* sanity check the ResourceOwnerDesc */
535 Assert(kind->release_phase != 0);
536 Assert(kind->release_priority != 0);
537
538 /*
539 * Mustn't try to remember more resources after we have already started
540 * releasing. We already checked this in ResourceOwnerEnlarge.
541 */
542 Assert(!owner->releasing);
543 Assert(!owner->sorted);
544
545 if (owner->narr >= RESOWNER_ARRAY_SIZE)
546 {
547 /* forgot to call ResourceOwnerEnlarge? */
548 elog(ERROR, "ResourceOwnerRemember called but array was full");
549 }
550
551 /* Append to the array. */
552 idx = owner->narr;
553 owner->arr[idx].item = value;
554 owner->arr[idx].kind = kind;
555 owner->narr++;
556}
557
558/*
559 * Forget that an object is owned by a ResourceOwner
560 *
561 * Note: If same resource ID is associated with the ResourceOwner more than
562 * once, one instance is removed.
563 *
564 * Note: Forgetting a resource does not guarantee that there is room to
565 * remember a new resource. One exception is when you forget the most
566 * recently remembered resource; that does make room for a new remember call.
567 * Some code callers rely on that exception.
568 */
569void
571{
572 /*
573 * Mustn't call this after we have already started releasing resources.
574 * (Release callback functions are not allowed to release additional
575 * resources.)
576 */
577 if (owner->releasing)
578 elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
579 Assert(!owner->sorted);
580
581 /* Search through all items in the array first. */
582 for (int i = owner->narr - 1; i >= 0; i--)
583 {
584 if (owner->arr[i].item == value &&
585 owner->arr[i].kind == kind)
586 {
587 owner->arr[i] = owner->arr[owner->narr - 1];
588 owner->narr--;
589
590#ifdef RESOWNER_STATS
592#endif
593 return;
594 }
595 }
596
597 /* Search hash */
598 if (owner->nhash > 0)
599 {
600 uint32 mask = owner->capacity - 1;
601 uint32 idx;
602
603 idx = hash_resource_elem(value, kind) & mask;
604 for (uint32 i = 0; i < owner->capacity; i++)
605 {
606 if (owner->hash[idx].item == value &&
607 owner->hash[idx].kind == kind)
608 {
609 owner->hash[idx].item = (Datum) 0;
610 owner->hash[idx].kind = NULL;
611 owner->nhash--;
612
613#ifdef RESOWNER_STATS
615#endif
616 return;
617 }
618 idx = (idx + 1) & mask;
619 }
620 }
621
622 /*
623 * Use %p to print the reference, since most objects tracked by a resource
624 * owner are pointers. It's a bit misleading if it's not a pointer, but
625 * this is a programmer error, anyway.
626 */
627 elog(ERROR, "%s %p is not owned by resource owner %s",
628 kind->name, DatumGetPointer(value), owner->name);
629}
630
631/*
632 * ResourceOwnerRelease
633 * Release all resources owned by a ResourceOwner and its descendants,
634 * but don't delete the owner objects themselves.
635 *
636 * Note that this executes just one phase of release, and so typically
637 * must be called three times. We do it this way because (a) we want to
638 * do all the recursion separately for each phase, thereby preserving
639 * the needed order of operations; and (b) xact.c may have other operations
640 * to do between the phases.
641 *
642 * phase: release phase to execute
643 * isCommit: true for successful completion of a query or transaction,
644 * false for unsuccessful
645 * isTopLevel: true if completing a main transaction, else false
646 *
647 * isCommit is passed because some modules may expect that their resources
648 * were all released already if the transaction or portal finished normally.
649 * If so it is reasonable to give a warning (NOT an error) should any
650 * unreleased resources be present. When isCommit is false, such warnings
651 * are generally inappropriate.
652 *
653 * isTopLevel is passed when we are releasing TopTransactionResourceOwner
654 * at completion of a main transaction. This generally means that *all*
655 * resources will be released, and so we can optimize things a bit.
656 *
657 * NOTE: After starting the release process, by calling this function, no new
658 * resources can be remembered in the resource owner. You also cannot call
659 * ResourceOwnerForget on any previously remembered resources to release
660 * resources "in retail" after that, you must let the bulk release take care
661 * of them.
662 */
663void
666 bool isCommit,
667 bool isTopLevel)
668{
669 /* There's not currently any setup needed before recursing */
671
672#ifdef RESOWNER_STATS
673 if (isTopLevel)
674 {
675 elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d",
677 narray_lookups = 0;
678 nhash_lookups = 0;
679 }
680#endif
681}
682
683static void
686 bool isCommit,
687 bool isTopLevel)
688{
689 ResourceOwner child;
693
694 /* Recurse to handle descendants */
695 for (child = owner->firstchild; child != NULL; child = child->nextchild)
697
698 /*
699 * To release the resources in the right order, sort them by phase and
700 * priority.
701 *
702 * The ReleaseResource callback functions are not allowed to remember or
703 * forget any other resources after this. Otherwise we lose track of where
704 * we are in processing the hash/array.
705 */
706 if (!owner->releasing)
707 {
709 Assert(!owner->sorted);
710 owner->releasing = true;
711 }
712 else
713 {
714 /*
715 * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not
716 * the first call to ResourceOwnerRelease. But if an error happens
717 * between the release phases, we might get called again for the same
718 * ResourceOwner from AbortTransaction.
719 */
720 }
721 if (!owner->sorted)
722 {
723 ResourceOwnerSort(owner);
724 owner->sorted = true;
725 }
726
727 /*
728 * Make CurrentResourceOwner point to me, so that the release callback
729 * functions know which resource owner is been released.
730 */
732 CurrentResourceOwner = owner;
733
735 {
736 /*
737 * Release all resources that need to be released before the locks.
738 *
739 * During a commit, there shouldn't be any remaining resources ---
740 * that would indicate failure to clean up the executor correctly ---
741 * so issue warnings. In the abort case, just clean up quietly.
742 */
743 ResourceOwnerReleaseAll(owner, phase, isCommit);
744
745 while (!dlist_is_empty(&owner->aio_handles))
746 {
747 dlist_node *node = dlist_head_node(&owner->aio_handles);
748
750 }
751 }
752 else if (phase == RESOURCE_RELEASE_LOCKS)
753 {
754 if (isTopLevel)
755 {
756 /*
757 * For a top-level xact we are going to release all locks (or at
758 * least all non-session locks), so just do a single lmgr call at
759 * the top of the recursion.
760 */
761 if (owner == TopTransactionResourceOwner)
762 {
765 }
766 }
767 else
768 {
769 /*
770 * Release locks retail. Note that if we are committing a
771 * subtransaction, we do NOT release its locks yet, but transfer
772 * them to the parent.
773 */
774 LOCALLOCK **locks;
775 int nlocks;
776
777 Assert(owner->parent != NULL);
778
779 /*
780 * Pass the list of locks owned by this resource owner to the lock
781 * manager, unless it has overflowed.
782 */
783 if (owner->nlocks > MAX_RESOWNER_LOCKS)
784 {
785 locks = NULL;
786 nlocks = 0;
787 }
788 else
789 {
790 locks = owner->locks;
791 nlocks = owner->nlocks;
792 }
793
794 if (isCommit)
795 LockReassignCurrentOwner(locks, nlocks);
796 else
797 LockReleaseCurrentOwner(locks, nlocks);
798 }
799 }
800 else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
801 {
802 /*
803 * Release all resources that need to be released after the locks.
804 */
805 ResourceOwnerReleaseAll(owner, phase, isCommit);
806 }
807
808 /* Let add-on modules get a chance too */
809 for (item = ResourceRelease_callbacks; item; item = next)
810 {
811 /* allow callbacks to unregister themselves when called */
812 next = item->next;
813 item->callback(phase, isCommit, isTopLevel, item->arg);
814 }
815
817}
818
819/*
820 * ResourceOwnerReleaseAllOfKind
821 * Release all resources of a certain type held by this owner.
822 */
823void
825{
826 /* Mustn't call this after we have already started releasing resources. */
827 if (owner->releasing)
828 elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
829 Assert(!owner->sorted);
830
831 /*
832 * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember
833 * while we're scanning the owner. Enlarging the hash would cause us to
834 * lose track of the point we're scanning.
835 */
836 owner->releasing = true;
837
838 /* Array first */
839 for (int i = 0; i < owner->narr; i++)
840 {
841 if (owner->arr[i].kind == kind)
842 {
843 Datum value = owner->arr[i].item;
844
845 owner->arr[i] = owner->arr[owner->narr - 1];
846 owner->narr--;
847 i--;
848
849 kind->ReleaseResource(value);
850 }
851 }
852
853 /* Then hash */
854 for (int i = 0; i < owner->capacity; i++)
855 {
856 if (owner->hash[i].kind == kind)
857 {
858 Datum value = owner->hash[i].item;
859
860 owner->hash[i].item = (Datum) 0;
861 owner->hash[i].kind = NULL;
862 owner->nhash--;
863
864 kind->ReleaseResource(value);
865 }
866 }
867 owner->releasing = false;
868}
869
870/*
871 * ResourceOwnerDelete
872 * Delete an owner object and its descendants.
873 *
874 * The caller must have already released all resources in the object tree.
875 */
876void
878{
879 /* We had better not be deleting CurrentResourceOwner ... */
881
882 /* And it better not own any resources, either */
883 Assert(owner->narr == 0);
884 Assert(owner->nhash == 0);
885 Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
886
887 /*
888 * Delete children. The recursive call will delink the child from me, so
889 * just iterate as long as there is a child.
890 */
891 while (owner->firstchild != NULL)
893
894 /*
895 * We delink the owner from its parent before deleting it, so that if
896 * there's an error we won't have deleted/busted owners still attached to
897 * the owner tree. Better a leak than a crash.
898 */
900
901 /* And free the object. */
902 if (owner->hash)
903 pfree(owner->hash);
904 pfree(owner);
905}
906
907/*
908 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
909 */
912{
913 return owner->parent;
914}
915
916/*
917 * Reassign a ResourceOwner to have a new parent
918 */
919void
922{
924
925 if (oldparent)
926 {
927 if (owner == oldparent->firstchild)
929 else
930 {
931 ResourceOwner child;
932
933 for (child = oldparent->firstchild; child; child = child->nextchild)
934 {
935 if (owner == child->nextchild)
936 {
937 child->nextchild = owner->nextchild;
938 break;
939 }
940 }
941 }
942 }
943
944 if (newparent)
945 {
946 Assert(owner != newparent);
947 owner->parent = newparent;
949 newparent->firstchild = owner;
950 }
951 else
952 {
953 owner->parent = NULL;
954 owner->nextchild = NULL;
955 }
956}
957
958/*
959 * Register or deregister callback functions for resource cleanup
960 *
961 * These functions can be used by dynamically loaded modules. These used
962 * to be the only way for an extension to register custom resource types
963 * with a resource owner, but nowadays it is easier to define a new
964 * ResourceOwnerDesc with custom callbacks.
965 */
966void
968{
970
974 item->callback = callback;
975 item->arg = arg;
978}
979
980void
982{
985
986 prev = NULL;
987 for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
988 {
989 if (item->callback == callback && item->arg == arg)
990 {
991 if (prev)
992 prev->next = item->next;
993 else
995 pfree(item);
996 break;
997 }
998 }
999}
1000
1001/*
1002 * Establish an AuxProcessResourceOwner for the current process.
1003 */
1004void
1006{
1009 AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
1011
1012 /*
1013 * Register a shmem-exit callback for cleanup of aux-process resource
1014 * owner. (This needs to run after, e.g., ShutdownXLOG.)
1015 */
1017}
1018
1019/*
1020 * Convenience routine to release all resources tracked in
1021 * AuxProcessResourceOwner (but that resowner is not destroyed here).
1022 * Warn about leaked resources if isCommit is true.
1023 */
1024void
1026{
1027 /*
1028 * At this writing, the only thing that could actually get released is
1029 * buffer pins; but we may as well do the full release protocol.
1030 */
1033 isCommit, true);
1036 isCommit, true);
1039 isCommit, true);
1040 /* allow it to be reused */
1043}
1044
1045/*
1046 * Shmem-exit callback for the same.
1047 * Warn about leaked resources if process exit code is zero (ie normal).
1048 */
1049static void
1051{
1052 bool isCommit = (code == 0);
1053
1055}
1056
1057/*
1058 * Remember that a Local Lock is owned by a ResourceOwner
1059 *
1060 * This is different from the generic ResourceOwnerRemember in that the list of
1061 * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
1062 * and when it overflows, we stop tracking locks. The point of only remembering
1063 * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
1064 * ResourceOwnerForgetLock doesn't need to scan through a large array to find
1065 * the entry.
1066 */
1067void
1069{
1070 Assert(locallock != NULL);
1071
1072 if (owner->nlocks > MAX_RESOWNER_LOCKS)
1073 return; /* we have already overflowed */
1074
1075 if (owner->nlocks < MAX_RESOWNER_LOCKS)
1076 owner->locks[owner->nlocks] = locallock;
1077 else
1078 {
1079 /* overflowed */
1080 }
1081 owner->nlocks++;
1082}
1083
1084/*
1085 * Forget that a Local Lock is owned by a ResourceOwner
1086 */
1087void
1089{
1090 int i;
1091
1092 if (owner->nlocks > MAX_RESOWNER_LOCKS)
1093 return; /* we have overflowed */
1094
1095 Assert(owner->nlocks > 0);
1096 for (i = owner->nlocks - 1; i >= 0; i--)
1097 {
1098 if (locallock == owner->locks[i])
1099 {
1100 owner->locks[i] = owner->locks[owner->nlocks - 1];
1101 owner->nlocks--;
1102 return;
1103 }
1104 }
1105 elog(ERROR, "lock reference %p is not owned by resource owner %s",
1106 locallock, owner->name);
1107}
1108
1109void
1111{
1113}
1114
1115void
1117{
1119}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
void pgaio_io_release_resowner(dlist_node *ioh_node, bool on_error)
Definition aio.c:266
static int32 next
Definition blutils.c:225
uint8_t uint8
Definition c.h:681
#define Assert(condition)
Definition c.h:1002
uint64_t uint64
Definition c.h:684
uint32_t uint32
Definition c.h:683
Datum arg
Definition elog.c:1323
#define LOG
Definition elog.h:32
#define WARNING
Definition elog.h:37
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
static uint64 hash_combine64(uint64 a, uint64 b)
Definition hashfn.h:80
static uint64 murmurhash64(uint64 data)
Definition hashfn.h:106
static void dlist_init(dlist_head *head)
Definition ilist.h:314
static void dlist_delete_from(dlist_head *head, dlist_node *node)
Definition ilist.h:429
static dlist_node * dlist_head_node(dlist_head *head)
Definition ilist.h:565
static bool dlist_is_empty(const dlist_head *head)
Definition ilist.h:336
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition ilist.h:364
#define nitems(x)
Definition indent.h:31
static struct @175 value
static int pg_cmp_u32(uint32 a, uint32 b)
Definition int.h:719
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition ipc.c:372
int b
Definition isn.c:74
int a
Definition isn.c:73
int i
Definition isn.c:77
void LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks)
Definition lock.c:2714
void LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks)
Definition lock.c:2619
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1235
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269
void pfree(void *pointer)
Definition mcxt.c:1619
MemoryContext TopMemoryContext
Definition mcxt.c:167
#define qsort(a, b, c, d)
Definition port.h:496
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
void ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe)
Definition predicate.c:3241
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
static unsigned hash(unsigned *uv, int n)
Definition rege_dfa.c:724
void ResourceOwnerNewParent(ResourceOwner owner, ResourceOwner newparent)
Definition resowner.c:921
#define MAX_RESOWNER_LOCKS
Definition resowner.c:107
void ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
Definition resowner.c:1069
static void ReleaseAuxProcessResourcesCallback(int code, Datum arg)
Definition resowner.c:1051
ResourceOwner TopTransactionResourceOwner
Definition resowner.c:175
#define RESOWNER_HASH_INIT_SIZE
Definition resowner.c:79
static void ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase, bool printLeakWarnings)
Definition resowner.c:345
void UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
Definition resowner.c:982
ResourceOwner ResourceOwnerCreate(ResourceOwner parent, const char *name)
Definition resowner.c:428
#define RESOWNER_HASH_MAX_ITEMS(capacity)
Definition resowner.c:91
void ReleaseAuxProcessResources(bool isCommit)
Definition resowner.c:1026
ResourceOwner ResourceOwnerGetParent(ResourceOwner owner)
Definition resowner.c:912
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
void ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
Definition resowner.c:1111
static uint32 hash_resource_elem(Datum value, const ResourceOwnerDesc *kind)
Definition resowner.c:222
static ResourceReleaseCallbackItem * ResourceRelease_callbacks
Definition resowner.c:195
static void ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition resowner.c:242
void CreateAuxProcessResourceOwner(void)
Definition resowner.c:1006
void ResourceOwnerRelease(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
Definition resowner.c:665
static int resource_priority_cmp(const void *a, const void *b)
Definition resowner.c:266
void RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
Definition resowner.c:968
static void ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel)
Definition resowner.c:685
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition resowner.c:571
void ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
Definition resowner.c:825
ResourceOwner AuxProcessResourceOwner
Definition resowner.c:176
void ResourceOwnerDelete(ResourceOwner owner)
Definition resowner.c:878
ResourceOwner CurTransactionResourceOwner
Definition resowner.c:174
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition resowner.c:531
#define RESOWNER_ARRAY_SIZE
Definition resowner.c:73
static void ResourceOwnerSort(ResourceOwner owner)
Definition resowner.c:289
void ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
Definition resowner.c:1089
void ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
Definition resowner.c:1117
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition resowner.c:459
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:896
Datum item
Definition resowner.c:66
const ResourceOwnerDesc * kind
Definition resowner.c:67
ResourceOwner parent
Definition resowner.c:114
dlist_head aio_handles
Definition resowner.c:165
const char * name
Definition resowner.c:117
LOCALLOCK * locks[MAX_RESOWNER_LOCKS]
Definition resowner.c:159
ResourceOwner nextchild
Definition resowner.c:116
ResourceOwner firstchild
Definition resowner.c:115
ResourceElem * hash
Definition resowner.c:154
ResourceElem arr[RESOWNER_ARRAY_SIZE]
Definition resowner.c:142
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:190
ResourceReleaseCallback callback
Definition resowner.c:191
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
static ItemArray items
const char * name

Typedef Documentation

◆ ResourceElem

◆ ResourceReleaseCallbackItem

Function Documentation

◆ CreateAuxProcessResourceOwner()

void CreateAuxProcessResourceOwner ( void  )

Definition at line 1006 of file resowner.c.

1007{
1010 AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
1012
1013 /*
1014 * Register a shmem-exit callback for cleanup of aux-process resource
1015 * owner. (This needs to run after, e.g., ShutdownXLOG.)
1016 */
1018}

References Assert, AuxProcessResourceOwner, CurrentResourceOwner, fb(), on_shmem_exit(), ReleaseAuxProcessResourcesCallback(), and ResourceOwnerCreate().

Referenced by AuxiliaryProcessMainCommon(), InitPostgres(), and InitWalSender().

◆ hash_resource_elem()

static uint32 hash_resource_elem ( Datum  value,
const ResourceOwnerDesc kind 
)
inlinestatic

Definition at line 222 of file resowner.c.

223{
224 /*
225 * Most resource kinds store a pointer in 'value', and pointers are unique
226 * all on their own. But some resources store plain integers (Files and
227 * Buffers as of this writing), so we want to incorporate the 'kind' in
228 * the hash too, otherwise those resources will collide a lot. But
229 * because there are only a few resource kinds like that - and only a few
230 * resource kinds to begin with - we don't need to work too hard to mix
231 * 'kind' into the hash. Just add it with hash_combine(), it perturbs the
232 * result enough for our purposes.
233 */
235 (uint64) (uintptr_t) kind);
236}

References fb(), hash_combine64(), murmurhash64(), and value.

Referenced by ResourceOwnerAddToHash(), and ResourceOwnerForget().

◆ RegisterResourceReleaseCallback()

◆ ReleaseAuxProcessResources()

◆ ReleaseAuxProcessResourcesCallback()

static void ReleaseAuxProcessResourcesCallback ( int  code,
Datum  arg 
)
static

Definition at line 1051 of file resowner.c.

1052{
1053 bool isCommit = (code == 0);
1054
1056}

References fb(), and ReleaseAuxProcessResources().

Referenced by CreateAuxProcessResourceOwner().

◆ resource_priority_cmp()

static int resource_priority_cmp ( const void a,
const void b 
)
static

Definition at line 266 of file resowner.c.

267{
268 const ResourceElem *ra = (const ResourceElem *) a;
269 const ResourceElem *rb = (const ResourceElem *) b;
270
271 /* Note: reverse order */
272 if (ra->kind->release_phase == rb->kind->release_phase)
273 return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority);
274 else if (ra->kind->release_phase > rb->kind->release_phase)
275 return -1;
276 else
277 return 1;
278}

References a, b, fb(), and pg_cmp_u32().

Referenced by ResourceOwnerSort().

◆ ResourceOwnerAddToHash()

static void ResourceOwnerAddToHash ( ResourceOwner  owner,
Datum  value,
const ResourceOwnerDesc kind 
)
static

Definition at line 242 of file resowner.c.

243{
244 uint32 mask = owner->capacity - 1;
245 uint32 idx;
246
247 Assert(kind != NULL);
248
249 /* Insert into first free slot at or after hash location. */
250 idx = hash_resource_elem(value, kind) & mask;
251 for (;;)
252 {
253 if (owner->hash[idx].kind == NULL)
254 break; /* found a free slot */
255 idx = (idx + 1) & mask;
256 }
257 owner->hash[idx].item = value;
258 owner->hash[idx].kind = kind;
259 owner->nhash++;
260}

References Assert, ResourceOwnerData::capacity, fb(), ResourceOwnerData::hash, hash_resource_elem(), idx(), ResourceElem::item, ResourceElem::kind, ResourceOwnerData::nhash, and value.

Referenced by ResourceOwnerEnlarge().

◆ ResourceOwnerCreate()

◆ ResourceOwnerDelete()

void ResourceOwnerDelete ( ResourceOwner  owner)

Definition at line 878 of file resowner.c.

879{
880 /* We had better not be deleting CurrentResourceOwner ... */
882
883 /* And it better not own any resources, either */
884 Assert(owner->narr == 0);
885 Assert(owner->nhash == 0);
886 Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
887
888 /*
889 * Delete children. The recursive call will delink the child from me, so
890 * just iterate as long as there is a child.
891 */
892 while (owner->firstchild != NULL)
894
895 /*
896 * We delink the owner from its parent before deleting it, so that if
897 * there's an error we won't have deleted/busted owners still attached to
898 * the owner tree. Better a leak than a crash.
899 */
901
902 /* And free the object. */
903 if (owner->hash)
904 pfree(owner->hash);
905 pfree(owner);
906}

References Assert, CurrentResourceOwner, fb(), ResourceOwnerData::firstchild, ResourceOwnerData::hash, MAX_RESOWNER_LOCKS, ResourceOwnerData::narr, ResourceOwnerData::nhash, ResourceOwnerData::nlocks, pfree(), ResourceOwnerDelete(), and ResourceOwnerNewParent().

Referenced by CleanupSubTransaction(), CleanupTransaction(), CommitSubTransaction(), CommitTransaction(), plpgsql_call_handler(), plpgsql_inline_handler(), PortalDrop(), PrepareTransaction(), ResourceOwnerDelete(), test_dsa_resowners(), test_resowner_leak(), test_resowner_many(), and test_resowner_priorities().

◆ ResourceOwnerEnlarge()

void ResourceOwnerEnlarge ( ResourceOwner  owner)

Definition at line 459 of file resowner.c.

460{
461 /*
462 * Mustn't try to remember more resources after we have already started
463 * releasing
464 */
465 if (owner->releasing)
466 elog(ERROR, "ResourceOwnerEnlarge called after release started");
467
468 if (owner->narr < RESOWNER_ARRAY_SIZE)
469 return; /* no work needed */
470
471 /*
472 * Is there space in the hash? If not, enlarge it.
473 */
474 if (owner->narr + owner->nhash >= owner->grow_at)
475 {
476 uint32 i,
477 oldcap,
478 newcap;
481
482 oldhash = owner->hash;
483 oldcap = owner->capacity;
484
485 /* Double the capacity (it must stay a power of 2!) */
488 newcap * sizeof(ResourceElem));
489
490 /*
491 * We assume we can't fail below this point, so OK to scribble on the
492 * owner
493 */
494 owner->hash = newhash;
495 owner->capacity = newcap;
497 owner->nhash = 0;
498
499 if (oldhash != NULL)
500 {
501 /*
502 * Transfer any pre-existing entries into the new hash table; they
503 * don't necessarily go where they were before, so this simple
504 * logic is the best way.
505 */
506 for (i = 0; i < oldcap; i++)
507 {
508 if (oldhash[i].kind != NULL)
509 ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
510 }
511
512 /* And release old hash table. */
513 pfree(oldhash);
514 }
515 }
516
517 /* Move items from the array to the hash */
518 for (int i = 0; i < owner->narr; i++)
519 ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
520 owner->narr = 0;
521
522 Assert(owner->nhash <= owner->grow_at);
523}

References ResourceOwnerData::arr, Assert, ResourceOwnerData::capacity, elog, ERROR, fb(), ResourceOwnerData::grow_at, ResourceOwnerData::hash, i, ResourceElem::item, ResourceElem::kind, MemoryContextAllocZero(), ResourceOwnerData::narr, ResourceOwnerData::nhash, pfree(), ResourceOwnerData::releasing, ResourceOwnerAddToHash(), RESOWNER_ARRAY_SIZE, RESOWNER_HASH_INIT_SIZE, RESOWNER_HASH_MAX_ITEMS, and TopMemoryContext.

Referenced by BufferAlloc(), CachedPlanAllowsSimpleValidityCheck(), CachedPlanIsSimplyValid(), CreateWaitEventSet(), dsm_create_descriptor(), dsm_unpin_mapping(), EvictAllUnpinnedBuffers(), EvictRelUnpinnedBuffers(), EvictUnpinnedBuffer(), ExtendBufferedRelLocal(), ExtendBufferedRelShared(), FlushDatabaseBuffers(), FlushRelationBuffers(), FlushRelationsAllBuffers(), GetCachedPlan(), GetLocalVictimBuffer(), GetVictimBuffer(), IncrBufferRefCount(), IncrTupleDescRefCount(), llvm_create_context(), LocalBufferAlloc(), MarkDirtyAllUnpinnedBuffers(), MarkDirtyRelUnpinnedBuffers(), MarkDirtyUnpinnedBuffer(), OpenTemporaryFile(), PathNameCreateTemporaryFile(), PathNameOpenTemporaryFile(), pg_cryptohash_create(), pg_hmac_create(), px_find_cipher(), px_find_digest(), ReadRecentBuffer(), RegisterSnapshotOnOwner(), RelationIncrementReferenceCount(), RememberManyTestResources(), SearchCatCacheInternal(), SearchCatCacheList(), SearchCatCacheMiss(), StartSharedBufferIO(), SyncOneBuffer(), test_resowner_forget_between_phases(), test_resowner_leak(), test_resowner_priorities(), and test_resowner_remember_between_phases().

◆ ResourceOwnerForget()

void ResourceOwnerForget ( ResourceOwner  owner,
Datum  value,
const ResourceOwnerDesc kind 
)

Definition at line 571 of file resowner.c.

572{
573 /*
574 * Mustn't call this after we have already started releasing resources.
575 * (Release callback functions are not allowed to release additional
576 * resources.)
577 */
578 if (owner->releasing)
579 elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
580 Assert(!owner->sorted);
581
582 /* Search through all items in the array first. */
583 for (int i = owner->narr - 1; i >= 0; i--)
584 {
585 if (owner->arr[i].item == value &&
586 owner->arr[i].kind == kind)
587 {
588 owner->arr[i] = owner->arr[owner->narr - 1];
589 owner->narr--;
590
591#ifdef RESOWNER_STATS
593#endif
594 return;
595 }
596 }
597
598 /* Search hash */
599 if (owner->nhash > 0)
600 {
601 uint32 mask = owner->capacity - 1;
602 uint32 idx;
603
604 idx = hash_resource_elem(value, kind) & mask;
605 for (uint32 i = 0; i < owner->capacity; i++)
606 {
607 if (owner->hash[idx].item == value &&
608 owner->hash[idx].kind == kind)
609 {
610 owner->hash[idx].item = (Datum) 0;
611 owner->hash[idx].kind = NULL;
612 owner->nhash--;
613
614#ifdef RESOWNER_STATS
616#endif
617 return;
618 }
619 idx = (idx + 1) & mask;
620 }
621 }
622
623 /*
624 * Use %p to print the reference, since most objects tracked by a resource
625 * owner are pointers. It's a bit misleading if it's not a pointer, but
626 * this is a programmer error, anyway.
627 */
628 elog(ERROR, "%s %p is not owned by resource owner %s",
629 kind->name, DatumGetPointer(value), owner->name);
630}

References ResourceOwnerData::arr, Assert, ResourceOwnerData::capacity, DatumGetPointer(), elog, ERROR, fb(), ResourceOwnerData::hash, hash_resource_elem(), i, idx(), ResourceElem::item, ResourceElem::kind, ResourceOwnerData::name, ResourceOwnerDesc::name, ResourceOwnerData::narr, ResourceOwnerData::nhash, ResourceOwnerData::releasing, ResourceOwnerData::sorted, and value.

Referenced by ForgetManyTestResources(), ResourceOwnerForgetBuffer(), ResourceOwnerForgetBufferIO(), ResourceOwnerForgetCatCacheListRef(), ResourceOwnerForgetCatCacheRef(), ResourceOwnerForgetCryptoHash(), ResourceOwnerForgetDSM(), ResourceOwnerForgetFile(), ResourceOwnerForgetHMAC(), ResourceOwnerForgetJIT(), ResourceOwnerForgetOSSLCipher(), ResourceOwnerForgetOSSLDigest(), ResourceOwnerForgetPlanCacheRef(), ResourceOwnerForgetRelationRef(), ResourceOwnerForgetSnapshot(), ResourceOwnerForgetTupleDesc(), ResourceOwnerForgetWaitEventSet(), and test_resowner_forget_between_phases().

◆ ResourceOwnerForgetAioHandle()

void ResourceOwnerForgetAioHandle ( ResourceOwner  owner,
struct dlist_node ioh_node 
)

Definition at line 1117 of file resowner.c.

1118{
1120}

References ResourceOwnerData::aio_handles, dlist_delete_from(), and fb().

Referenced by pgaio_io_reclaim(), and pgaio_io_release_resowner().

◆ ResourceOwnerForgetLock()

void ResourceOwnerForgetLock ( ResourceOwner  owner,
LOCALLOCK locallock 
)

Definition at line 1089 of file resowner.c.

1090{
1091 int i;
1092
1093 if (owner->nlocks > MAX_RESOWNER_LOCKS)
1094 return; /* we have overflowed */
1095
1096 Assert(owner->nlocks > 0);
1097 for (i = owner->nlocks - 1; i >= 0; i--)
1098 {
1099 if (locallock == owner->locks[i])
1100 {
1101 owner->locks[i] = owner->locks[owner->nlocks - 1];
1102 owner->nlocks--;
1103 return;
1104 }
1105 }
1106 elog(ERROR, "lock reference %p is not owned by resource owner %s",
1107 locallock, owner->name);
1108}

References Assert, elog, ERROR, fb(), i, ResourceOwnerData::locks, MAX_RESOWNER_LOCKS, ResourceOwnerData::name, and ResourceOwnerData::nlocks.

Referenced by LockReassignOwner(), LockRelease(), LockReleaseAll(), ReleaseLockIfHeld(), and RemoveLocalLock().

◆ ResourceOwnerGetParent()

ResourceOwner ResourceOwnerGetParent ( ResourceOwner  owner)

Definition at line 912 of file resowner.c.

913{
914 return owner->parent;
915}

References ResourceOwnerData::parent.

Referenced by LockReassignCurrentOwner().

◆ ResourceOwnerNewParent()

void ResourceOwnerNewParent ( ResourceOwner  owner,
ResourceOwner  newparent 
)

Definition at line 921 of file resowner.c.

923{
925
926 if (oldparent)
927 {
928 if (owner == oldparent->firstchild)
930 else
931 {
932 ResourceOwner child;
933
934 for (child = oldparent->firstchild; child; child = child->nextchild)
935 {
936 if (owner == child->nextchild)
937 {
938 child->nextchild = owner->nextchild;
939 break;
940 }
941 }
942 }
943 }
944
945 if (newparent)
946 {
947 Assert(owner != newparent);
948 owner->parent = newparent;
950 newparent->firstchild = owner;
951 }
952 else
953 {
954 owner->parent = NULL;
955 owner->nextchild = NULL;
956 }
957}

References Assert, fb(), ResourceOwnerData::firstchild, ResourceOwnerData::nextchild, and ResourceOwnerData::parent.

Referenced by AtSubAbort_Portals(), AtSubCommit_Portals(), and ResourceOwnerDelete().

◆ ResourceOwnerRelease()

void ResourceOwnerRelease ( ResourceOwner  owner,
ResourceReleasePhase  phase,
bool  isCommit,
bool  isTopLevel 
)

Definition at line 665 of file resowner.c.

669{
670 /* There's not currently any setup needed before recursing */
672
673#ifdef RESOWNER_STATS
674 if (isTopLevel)
675 {
676 elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d",
678 narray_lookups = 0;
679 nhash_lookups = 0;
680 }
681#endif
682}

References elog, fb(), LOG, and ResourceOwnerReleaseInternal().

Referenced by AbortSubTransaction(), AbortTransaction(), CommitSubTransaction(), CommitTransaction(), PortalDrop(), PrepareTransaction(), ReleaseAuxProcessResources(), test_dsa_resowners(), test_resowner_forget_between_phases(), test_resowner_leak(), test_resowner_many(), test_resowner_priorities(), and test_resowner_remember_between_phases().

◆ ResourceOwnerReleaseAll()

static void ResourceOwnerReleaseAll ( ResourceOwner  owner,
ResourceReleasePhase  phase,
bool  printLeakWarnings 
)
static

Definition at line 345 of file resowner.c.

347{
350 bool using_arr;
351
352 /*
353 * ResourceOwnerSort must've been called already. All the resources are
354 * either in the array or the hash.
355 */
356 Assert(owner->releasing);
357 Assert(owner->sorted);
358 if (owner->nhash == 0)
359 {
360 items = owner->arr;
361 nitems = owner->narr;
362 using_arr = true;
363 }
364 else
365 {
366 Assert(owner->narr == 0);
367 items = owner->hash;
368 nitems = owner->nhash;
369 using_arr = false;
370 }
371
372 /*
373 * The resources are sorted in reverse priority order. Release them
374 * starting from the end, until we hit the end of the phase that we are
375 * releasing now. We will continue from there when called again for the
376 * next phase.
377 */
378 while (nitems > 0)
379 {
380 uint32 idx = nitems - 1;
381 Datum value = items[idx].item;
382 const ResourceOwnerDesc *kind = items[idx].kind;
383
384 if (kind->release_phase > phase)
385 break;
386 Assert(kind->release_phase == phase);
387
389 {
390 char *res_str;
391
392 res_str = kind->DebugPrint ?
393 kind->DebugPrint(value)
394 : psprintf("%s %p", kind->name, DatumGetPointer(value));
395 elog(WARNING, "resource was not closed: %s", res_str);
396 pfree(res_str);
397 }
398
399 /*
400 * Update stored count to forget the item before calling its
401 * ReleaseResource method. This avoids double-free crashes in case an
402 * error gets thrown within ReleaseResource.
403 */
404 nitems--;
405 if (using_arr)
406 owner->narr = nitems;
407 else
408 owner->nhash = nitems;
409
410 kind->ReleaseResource(value);
411 }
412}

References ResourceOwnerData::arr, Assert, DatumGetPointer(), ResourceOwnerDesc::DebugPrint, elog, fb(), ResourceOwnerData::hash, idx(), items, ResourceOwnerDesc::name, ResourceOwnerData::narr, ResourceOwnerData::nhash, nitems, pfree(), psprintf(), ResourceOwnerDesc::release_phase, ResourceOwnerDesc::ReleaseResource, ResourceOwnerData::releasing, ResourceOwnerData::sorted, value, and WARNING.

Referenced by ResourceOwnerReleaseInternal().

◆ ResourceOwnerReleaseAllOfKind()

void ResourceOwnerReleaseAllOfKind ( ResourceOwner  owner,
const ResourceOwnerDesc kind 
)

Definition at line 825 of file resowner.c.

826{
827 /* Mustn't call this after we have already started releasing resources. */
828 if (owner->releasing)
829 elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
830 Assert(!owner->sorted);
831
832 /*
833 * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember
834 * while we're scanning the owner. Enlarging the hash would cause us to
835 * lose track of the point we're scanning.
836 */
837 owner->releasing = true;
838
839 /* Array first */
840 for (int i = 0; i < owner->narr; i++)
841 {
842 if (owner->arr[i].kind == kind)
843 {
844 Datum value = owner->arr[i].item;
845
846 owner->arr[i] = owner->arr[owner->narr - 1];
847 owner->narr--;
848 i--;
849
850 kind->ReleaseResource(value);
851 }
852 }
853
854 /* Then hash */
855 for (int i = 0; i < owner->capacity; i++)
856 {
857 if (owner->hash[i].kind == kind)
858 {
859 Datum value = owner->hash[i].item;
860
861 owner->hash[i].item = (Datum) 0;
862 owner->hash[i].kind = NULL;
863 owner->nhash--;
864
865 kind->ReleaseResource(value);
866 }
867 }
868 owner->releasing = false;
869}

References ResourceOwnerData::arr, Assert, ResourceOwnerData::capacity, elog, ERROR, fb(), ResourceOwnerData::hash, i, ResourceElem::item, ResourceElem::kind, ResourceOwnerDesc::name, ResourceOwnerData::narr, ResourceOwnerData::nhash, ResourceOwnerDesc::ReleaseResource, ResourceOwnerData::releasing, ResourceOwnerData::sorted, and value.

Referenced by ReleaseAllPlanCacheRefsInOwner().

◆ ResourceOwnerReleaseInternal()

static void ResourceOwnerReleaseInternal ( ResourceOwner  owner,
ResourceReleasePhase  phase,
bool  isCommit,
bool  isTopLevel 
)
static

Definition at line 685 of file resowner.c.

689{
690 ResourceOwner child;
694
695 /* Recurse to handle descendants */
696 for (child = owner->firstchild; child != NULL; child = child->nextchild)
698
699 /*
700 * To release the resources in the right order, sort them by phase and
701 * priority.
702 *
703 * The ReleaseResource callback functions are not allowed to remember or
704 * forget any other resources after this. Otherwise we lose track of where
705 * we are in processing the hash/array.
706 */
707 if (!owner->releasing)
708 {
710 Assert(!owner->sorted);
711 owner->releasing = true;
712 }
713 else
714 {
715 /*
716 * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not
717 * the first call to ResourceOwnerRelease. But if an error happens
718 * between the release phases, we might get called again for the same
719 * ResourceOwner from AbortTransaction.
720 */
721 }
722 if (!owner->sorted)
723 {
724 ResourceOwnerSort(owner);
725 owner->sorted = true;
726 }
727
728 /*
729 * Make CurrentResourceOwner point to me, so that the release callback
730 * functions know which resource owner is been released.
731 */
733 CurrentResourceOwner = owner;
734
736 {
737 /*
738 * Release all resources that need to be released before the locks.
739 *
740 * During a commit, there shouldn't be any remaining resources ---
741 * that would indicate failure to clean up the executor correctly ---
742 * so issue warnings. In the abort case, just clean up quietly.
743 */
744 ResourceOwnerReleaseAll(owner, phase, isCommit);
745
746 while (!dlist_is_empty(&owner->aio_handles))
747 {
748 dlist_node *node = dlist_head_node(&owner->aio_handles);
749
751 }
752 }
753 else if (phase == RESOURCE_RELEASE_LOCKS)
754 {
755 if (isTopLevel)
756 {
757 /*
758 * For a top-level xact we are going to release all locks (or at
759 * least all non-session locks), so just do a single lmgr call at
760 * the top of the recursion.
761 */
762 if (owner == TopTransactionResourceOwner)
763 {
766 }
767 }
768 else
769 {
770 /*
771 * Release locks retail. Note that if we are committing a
772 * subtransaction, we do NOT release its locks yet, but transfer
773 * them to the parent.
774 */
775 LOCALLOCK **locks;
776 int nlocks;
777
778 Assert(owner->parent != NULL);
779
780 /*
781 * Pass the list of locks owned by this resource owner to the lock
782 * manager, unless it has overflowed.
783 */
784 if (owner->nlocks > MAX_RESOWNER_LOCKS)
785 {
786 locks = NULL;
787 nlocks = 0;
788 }
789 else
790 {
791 locks = owner->locks;
792 nlocks = owner->nlocks;
793 }
794
795 if (isCommit)
796 LockReassignCurrentOwner(locks, nlocks);
797 else
798 LockReleaseCurrentOwner(locks, nlocks);
799 }
800 }
801 else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
802 {
803 /*
804 * Release all resources that need to be released after the locks.
805 */
806 ResourceOwnerReleaseAll(owner, phase, isCommit);
807 }
808
809 /* Let add-on modules get a chance too */
810 for (item = ResourceRelease_callbacks; item; item = next)
811 {
812 /* allow callbacks to unregister themselves when called */
813 next = item->next;
814 item->callback(phase, isCommit, isTopLevel, item->arg);
815 }
816
818}

References ResourceOwnerData::aio_handles, ResourceReleaseCallbackItem::arg, Assert, ResourceReleaseCallbackItem::callback, CurrentResourceOwner, dlist_head_node(), dlist_is_empty(), fb(), ResourceOwnerData::firstchild, LockReassignCurrentOwner(), LockReleaseCurrentOwner(), ResourceOwnerData::locks, MAX_RESOWNER_LOCKS, next, ResourceReleaseCallbackItem::next, ResourceOwnerData::nextchild, ResourceOwnerData::nlocks, ResourceOwnerData::parent, pgaio_io_release_resowner(), ProcReleaseLocks(), ReleasePredicateLocks(), ResourceOwnerData::releasing, RESOURCE_RELEASE_AFTER_LOCKS, RESOURCE_RELEASE_BEFORE_LOCKS, RESOURCE_RELEASE_LOCKS, ResourceOwnerReleaseAll(), ResourceOwnerReleaseInternal(), ResourceOwnerSort(), ResourceRelease_callbacks, ResourceOwnerData::sorted, and TopTransactionResourceOwner.

Referenced by ResourceOwnerRelease(), and ResourceOwnerReleaseInternal().

◆ ResourceOwnerRemember()

void ResourceOwnerRemember ( ResourceOwner  owner,
Datum  value,
const ResourceOwnerDesc kind 
)

Definition at line 531 of file resowner.c.

532{
533 uint32 idx;
534
535 /* sanity check the ResourceOwnerDesc */
536 Assert(kind->release_phase != 0);
537 Assert(kind->release_priority != 0);
538
539 /*
540 * Mustn't try to remember more resources after we have already started
541 * releasing. We already checked this in ResourceOwnerEnlarge.
542 */
543 Assert(!owner->releasing);
544 Assert(!owner->sorted);
545
546 if (owner->narr >= RESOWNER_ARRAY_SIZE)
547 {
548 /* forgot to call ResourceOwnerEnlarge? */
549 elog(ERROR, "ResourceOwnerRemember called but array was full");
550 }
551
552 /* Append to the array. */
553 idx = owner->narr;
554 owner->arr[idx].item = value;
555 owner->arr[idx].kind = kind;
556 owner->narr++;
557}

References ResourceOwnerData::arr, Assert, elog, ERROR, idx(), ResourceElem::item, ResourceElem::kind, ResourceOwnerData::narr, ResourceOwnerDesc::release_phase, ResourceOwnerDesc::release_priority, ResourceOwnerData::releasing, RESOWNER_ARRAY_SIZE, ResourceOwnerData::sorted, and value.

Referenced by RememberManyTestResources(), ResourceOwnerRememberBuffer(), ResourceOwnerRememberBufferIO(), ResourceOwnerRememberCatCacheListRef(), ResourceOwnerRememberCatCacheRef(), ResourceOwnerRememberCryptoHash(), ResourceOwnerRememberDSM(), ResourceOwnerRememberFile(), ResourceOwnerRememberHMAC(), ResourceOwnerRememberJIT(), ResourceOwnerRememberOSSLCipher(), ResourceOwnerRememberOSSLDigest(), ResourceOwnerRememberPlanCacheRef(), ResourceOwnerRememberRelationRef(), ResourceOwnerRememberSnapshot(), ResourceOwnerRememberTupleDesc(), ResourceOwnerRememberWaitEventSet(), test_resowner_forget_between_phases(), test_resowner_leak(), test_resowner_priorities(), and test_resowner_remember_between_phases().

◆ ResourceOwnerRememberAioHandle()

void ResourceOwnerRememberAioHandle ( ResourceOwner  owner,
struct dlist_node ioh_node 
)

Definition at line 1111 of file resowner.c.

1112{
1114}

References ResourceOwnerData::aio_handles, dlist_push_tail(), and fb().

Referenced by pgaio_io_resowner_register().

◆ ResourceOwnerRememberLock()

void ResourceOwnerRememberLock ( ResourceOwner  owner,
LOCALLOCK locallock 
)

Definition at line 1069 of file resowner.c.

1070{
1071 Assert(locallock != NULL);
1072
1073 if (owner->nlocks > MAX_RESOWNER_LOCKS)
1074 return; /* we have already overflowed */
1075
1076 if (owner->nlocks < MAX_RESOWNER_LOCKS)
1077 owner->locks[owner->nlocks] = locallock;
1078 else
1079 {
1080 /* overflowed */
1081 }
1082 owner->nlocks++;
1083}

References Assert, fb(), ResourceOwnerData::locks, MAX_RESOWNER_LOCKS, and ResourceOwnerData::nlocks.

Referenced by GrantLockLocal(), and LockReassignOwner().

◆ ResourceOwnerSort()

static void ResourceOwnerSort ( ResourceOwner  owner)
static

Definition at line 289 of file resowner.c.

290{
293
294 if (owner->nhash == 0)
295 {
296 items = owner->arr;
297 nitems = owner->narr;
298 }
299 else
300 {
301 /*
302 * Compact the hash table, so that all the elements are in the
303 * beginning of the 'hash' array, with no empty elements.
304 */
305 uint32 dst = 0;
306
307 for (int idx = 0; idx < owner->capacity; idx++)
308 {
309 if (owner->hash[idx].kind != NULL)
310 {
311 if (dst != idx)
312 owner->hash[dst] = owner->hash[idx];
313 dst++;
314 }
315 }
316
317 /*
318 * Move all entries from the fixed-size array to 'hash'.
319 *
320 * RESOWNER_HASH_MAX_ITEMS is defined so that there is always enough
321 * free space to move all the elements from the fixed-size array to
322 * the hash.
323 */
324 Assert(dst + owner->narr <= owner->capacity);
325 for (int idx = 0; idx < owner->narr; idx++)
326 {
327 owner->hash[dst] = owner->arr[idx];
328 dst++;
329 }
330 Assert(dst == owner->nhash + owner->narr);
331 owner->narr = 0;
332 owner->nhash = dst;
333
334 items = owner->hash;
335 nitems = owner->nhash;
336 }
337
339}

References ResourceOwnerData::arr, Assert, ResourceOwnerData::capacity, fb(), ResourceOwnerData::hash, idx(), items, ResourceElem::kind, ResourceOwnerData::narr, ResourceOwnerData::nhash, nitems, qsort, and resource_priority_cmp().

Referenced by ResourceOwnerReleaseInternal().

◆ StaticAssertDecl()

StaticAssertDecl ( RESOWNER_HASH_MAX_ITEMS(RESOWNER_HASH_INIT_SIZE) >=  RESOWNER_ARRAY_SIZE,
"initial hash size too small compared to array size"   
)

◆ UnregisterResourceReleaseCallback()

void UnregisterResourceReleaseCallback ( ResourceReleaseCallback  callback,
void arg 
)

Definition at line 982 of file resowner.c.

983{
986
987 prev = NULL;
988 for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
989 {
990 if (item->callback == callback && item->arg == arg)
991 {
992 if (prev)
993 prev->next = item->next;
994 else
996 pfree(item);
997 break;
998 }
999 }
1000}

References arg, ResourceReleaseCallbackItem::arg, ResourceReleaseCallbackItem::callback, callback(), fb(), ResourceReleaseCallbackItem::next, pfree(), and ResourceRelease_callbacks.

Variable Documentation

◆ AuxProcessResourceOwner

◆ CurrentResourceOwner

ResourceOwner CurrentResourceOwner = NULL

Definition at line 173 of file resowner.c.

Referenced by _SPI_execute_plan(), apply_spooled_messages(), AssignTransactionId(), AsyncReadBuffers(), AtAbort_ResourceOwner(), AtStart_ResourceOwner(), AtSubAbort_ResourceOwner(), AtSubStart_ResourceOwner(), attach_internal(), buffer_call_start_io(), buffer_stage_common(), BufferAlloc(), CleanupSubTransaction(), CleanupTransaction(), close_lo_relation(), CommitSubTransaction(), CommitTransaction(), create_internal(), CreateAuxProcessResourceOwner(), DecrTupleDescRefCount(), dsm_create_descriptor(), dsm_unpin_mapping(), EvictAllUnpinnedBuffers(), EvictRelUnpinnedBuffers(), EvictUnpinnedBuffer(), exec_eval_simple_expr(), exec_init_tuple_store(), exec_simple_check_plan(), exec_stmt_block(), ExecAppendAsyncEventWait(), ExplainExecuteQuery(), ExtendBufferedRelLocal(), ExtendBufferedRelShared(), extendBufFile(), FlushDatabaseBuffers(), FlushRelationBuffers(), FlushRelationsAllBuffers(), get_segment_by_index(), GetCurrentFDWTuplestore(), GetLocalVictimBuffer(), GetVictimBuffer(), handle_get(), handle_get_and_error(), handle_get_release(), handle_get_twice(), IncrBufferRefCount(), IncrTupleDescRefCount(), init_execution_state(), InitPostgres(), llvm_create_context(), LocalBufferAlloc(), lock_and_open_sequence(), LockAcquireExtended(), LockReassignCurrentOwner(), LockReassignOwner(), LockRelease(), LogicalSlotAdvanceAndCheckSnapState(), make_callstmt_target(), make_new_segment(), makeBufFileCommon(), MakeTransitionCaptureState(), MarkDirtyAllUnpinnedBuffers(), MarkDirtyRelUnpinnedBuffers(), MarkDirtyUnpinnedBuffer(), open_lo_relation(), OpenTemporaryFile(), PathNameCreateTemporaryFile(), PathNameOpenTemporaryFile(), perform_base_backup(), PersistHoldablePortal(), pg_cryptohash_create(), pg_hmac_create(), pg_logical_slot_get_changes_guts(), PinBuffer(), PinLocalBuffer(), plperl_spi_exec(), plperl_spi_exec_prepared(), plperl_spi_fetchrow(), plperl_spi_prepare(), plperl_spi_query(), plperl_spi_query_prepared(), plpgsql_estate_setup(), pltcl_func_handler(), pltcl_init_tuple_store(), pltcl_returnnext(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), pltcl_SPI_prepare(), pltcl_subtrans_abort(), pltcl_subtrans_commit(), pltcl_subtransaction(), PLy_abort_open_subtransactions(), PLy_cursor_fetch(), PLy_cursor_iternext(), PLy_cursor_plan(), PLy_cursor_query(), PLy_spi_execute_plan(), PLy_spi_execute_query(), PLy_spi_prepare(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_commit(), PLy_subtransaction_enter(), PLy_subtransaction_exit(), PopTransaction(), PortalCleanup(), PortalRun(), PortalRunFetch(), PortalStart(), PrepareTransaction(), px_find_cipher(), px_find_digest(), read_rel_block_ll(), ReadRecentBuffer(), RegisterSnapshot(), RegisterTemporaryFile(), RelationDecrementReferenceCount(), RelationIncrementReferenceCount(), ReleaseCatCache(), ReleaseCatCacheList(), ReleaseLockIfHeld(), ReorderBufferImmediateInvalidation(), ReorderBufferProcessTXN(), repack_setup_logical_decoding(), repack_store_change(), ResourceOwnerDelete(), ResourceOwnerReleaseInternal(), SearchCatCacheInternal(), SearchCatCacheList(), SearchCatCacheMiss(), ShutdownXLOG(), SnapBuildClearExportedSnapshot(), SnapBuildExportSnapshot(), SPI_plan_get_cached_plan(), StartSharedBufferIO(), StartupXLOG(), SyncOneBuffer(), TerminateBufferIO(), test_dsa_resowners(), test_resowner_forget_between_phases(), test_resowner_leak(), test_resowner_many(), test_resowner_priorities(), test_resowner_remember_between_phases(), TrackNewBufferPin(), tuplestore_begin_common(), tuplestore_puttuple_common(), UnlockReleaseBuffer(), UnpinBuffer(), UnpinLocalBuffer(), UnregisterSnapshot(), UploadManifest(), and WaitLatchOrSocket().

◆ CurTransactionResourceOwner

◆ ResourceRelease_callbacks

◆ TopTransactionResourceOwner