LCOV - code coverage report
Current view: top level - src/backend/utils/resowner - resowner.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 238 261 91.2 %
Date: 2025-08-17 01:17:32 Functions: 22 24 91.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.16