LCOV - code coverage report
Current view: top level - src/backend/utils/resowner - resowner.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 238 261 91.2 %
Date: 2025-04-01 15:15:16 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     3707842 : 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             : #if SIZEOF_DATUM == 8
     235     3707842 :     return hash_combine64(murmurhash64((uint64) value), (uint64) kind);
     236             : #else
     237             :     return hash_combine(murmurhash32((uint32) value), (uint32) kind);
     238             : #endif
     239             : }
     240             : 
     241             : /*
     242             :  * Adds 'value' of given 'kind' to the ResourceOwner's hash table
     243             :  */
     244             : static void
     245     2818782 : ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
     246             : {
     247     2818782 :     uint32      mask = owner->capacity - 1;
     248             :     uint32      idx;
     249             : 
     250             :     Assert(kind != NULL);
     251             : 
     252             :     /* Insert into first free slot at or after hash location. */
     253     2818782 :     idx = hash_resource_elem(value, kind) & mask;
     254             :     for (;;)
     255             :     {
     256   367724386 :         if (owner->hash[idx].kind == NULL)
     257     2818782 :             break;              /* found a free slot */
     258   364905604 :         idx = (idx + 1) & mask;
     259             :     }
     260     2818782 :     owner->hash[idx].item = value;
     261     2818782 :     owner->hash[idx].kind = kind;
     262     2818782 :     owner->nhash++;
     263     2818782 : }
     264             : 
     265             : /*
     266             :  * Comparison function to sort by release phase and priority
     267             :  */
     268             : static int
     269     1121178 : resource_priority_cmp(const void *a, const void *b)
     270             : {
     271     1121178 :     const ResourceElem *ra = (const ResourceElem *) a;
     272     1121178 :     const ResourceElem *rb = (const ResourceElem *) b;
     273             : 
     274             :     /* Note: reverse order */
     275     1121178 :     if (ra->kind->release_phase == rb->kind->release_phase)
     276      868858 :         return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority);
     277      252320 :     else if (ra->kind->release_phase > rb->kind->release_phase)
     278       28976 :         return -1;
     279             :     else
     280      223344 :         return 1;
     281             : }
     282             : 
     283             : /*
     284             :  * Sort resources in reverse release priority.
     285             :  *
     286             :  * If the hash table is in use, all the elements from the fixed-size array are
     287             :  * moved to the hash table, and then the hash table is sorted.  If there is no
     288             :  * hash table, then the fixed-size array is sorted directly.  In either case,
     289             :  * the result is one sorted array that contains all the resources.
     290             :  */
     291             : static void
     292     1606210 : ResourceOwnerSort(ResourceOwner owner)
     293             : {
     294             :     ResourceElem *items;
     295             :     uint32      nitems;
     296             : 
     297     1606210 :     if (owner->nhash == 0)
     298             :     {
     299     1606048 :         items = owner->arr;
     300     1606048 :         nitems = owner->narr;
     301             :     }
     302             :     else
     303             :     {
     304             :         /*
     305             :          * Compact the hash table, so that all the elements are in the
     306             :          * beginning of the 'hash' array, with no empty elements.
     307             :          */
     308         162 :         uint32      dst = 0;
     309             : 
     310     1083170 :         for (int idx = 0; idx < owner->capacity; idx++)
     311             :         {
     312     1083008 :             if (owner->hash[idx].kind != NULL)
     313             :             {
     314      416412 :                 if (dst != idx)
     315      416270 :                     owner->hash[dst] = owner->hash[idx];
     316      416412 :                 dst++;
     317             :             }
     318             :         }
     319             : 
     320             :         /*
     321             :          * Move all entries from the fixed-size array to 'hash'.
     322             :          *
     323             :          * RESOWNER_HASH_MAX_ITEMS is defined so that there is always enough
     324             :          * free space to move all the elements from the fixed-size array to
     325             :          * the hash.
     326             :          */
     327             :         Assert(dst + owner->narr <= owner->capacity);
     328        1026 :         for (int idx = 0; idx < owner->narr; idx++)
     329             :         {
     330         864 :             owner->hash[dst] = owner->arr[idx];
     331         864 :             dst++;
     332             :         }
     333             :         Assert(dst == owner->nhash + owner->narr);
     334         162 :         owner->narr = 0;
     335         162 :         owner->nhash = dst;
     336             : 
     337         162 :         items = owner->hash;
     338         162 :         nitems = owner->nhash;
     339             :     }
     340             : 
     341     1606210 :     qsort(items, nitems, sizeof(ResourceElem), resource_priority_cmp);
     342     1606210 : }
     343             : 
     344             : /*
     345             :  * Call the ReleaseResource callback on entries with given 'phase'.
     346             :  */
     347             : static void
     348     3212424 : ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase,
     349             :                         bool printLeakWarnings)
     350             : {
     351             :     ResourceElem *items;
     352             :     uint32      nitems;
     353             : 
     354             :     /*
     355             :      * ResourceOwnerSort must've been called already.  All the resources are
     356             :      * either in the array or the hash.
     357             :      */
     358             :     Assert(owner->releasing);
     359             :     Assert(owner->sorted);
     360     3212424 :     if (owner->nhash == 0)
     361             :     {
     362     3212100 :         items = owner->arr;
     363     3212100 :         nitems = owner->narr;
     364             :     }
     365             :     else
     366             :     {
     367             :         Assert(owner->narr == 0);
     368         324 :         items = owner->hash;
     369         324 :         nitems = owner->nhash;
     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     3762512 :     while (nitems > 0)
     379             :     {
     380      583740 :         uint32      idx = nitems - 1;
     381      583740 :         Datum       value = items[idx].item;
     382      583740 :         const ResourceOwnerDesc *kind = items[idx].kind;
     383             : 
     384      583740 :         if (kind->release_phase > phase)
     385       33652 :             break;
     386             :         Assert(kind->release_phase == phase);
     387             : 
     388      550088 :         if (printLeakWarnings)
     389             :         {
     390             :             char       *res_str;
     391             : 
     392           4 :             res_str = kind->DebugPrint ?
     393           2 :                 kind->DebugPrint(value)
     394           2 :                 : psprintf("%s %p", kind->name, DatumGetPointer(value));
     395           2 :             elog(WARNING, "resource was not closed: %s", res_str);
     396           2 :             pfree(res_str);
     397             :         }
     398      550088 :         kind->ReleaseResource(value);
     399      550088 :         nitems--;
     400             :     }
     401     3212424 :     if (owner->nhash == 0)
     402     3212100 :         owner->narr = nitems;
     403             :     else
     404         324 :         owner->nhash = nitems;
     405     3212424 : }
     406             : 
     407             : 
     408             : /*****************************************************************************
     409             :  *    EXPORTED ROUTINES                                                      *
     410             :  *****************************************************************************/
     411             : 
     412             : 
     413             : /*
     414             :  * ResourceOwnerCreate
     415             :  *      Create an empty ResourceOwner.
     416             :  *
     417             :  * All ResourceOwner objects are kept in TopMemoryContext, since they should
     418             :  * only be freed explicitly.
     419             :  */
     420             : ResourceOwner
     421     1607082 : ResourceOwnerCreate(ResourceOwner parent, const char *name)
     422             : {
     423             :     ResourceOwner owner;
     424             : 
     425     1607082 :     owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
     426             :                                                    sizeof(struct ResourceOwnerData));
     427     1607082 :     owner->name = name;
     428             : 
     429     1607082 :     if (parent)
     430             :     {
     431      776568 :         owner->parent = parent;
     432      776568 :         owner->nextchild = parent->firstchild;
     433      776568 :         parent->firstchild = owner;
     434             :     }
     435             : 
     436     1607082 :     dlist_init(&owner->aio_handles);
     437             : 
     438     1607082 :     return owner;
     439             : }
     440             : 
     441             : /*
     442             :  * Make sure there is room for at least one more resource in an array.
     443             :  *
     444             :  * This is separate from actually inserting a resource because if we run out
     445             :  * of memory, it's critical to do so *before* acquiring the resource.
     446             :  *
     447             :  * NB: Make sure there are no unrelated ResourceOwnerRemember() calls between
     448             :  * your ResourceOwnerEnlarge() call and the ResourceOwnerRemember() call that
     449             :  * you reserved the space for!
     450             :  */
     451             : void
     452   345101678 : ResourceOwnerEnlarge(ResourceOwner owner)
     453             : {
     454             :     /*
     455             :      * Mustn't try to remember more resources after we have already started
     456             :      * releasing
     457             :      */
     458   345101678 :     if (owner->releasing)
     459           2 :         elog(ERROR, "ResourceOwnerEnlarge called after release started");
     460             : 
     461   345101676 :     if (owner->narr < RESOWNER_ARRAY_SIZE)
     462   345060354 :         return;                 /* no work needed */
     463             : 
     464             :     /*
     465             :      * Is there space in the hash? If not, enlarge it.
     466             :      */
     467       41322 :     if (owner->narr + owner->nhash >= owner->grow_at)
     468             :     {
     469             :         uint32      i,
     470             :                     oldcap,
     471             :                     newcap;
     472             :         ResourceElem *oldhash;
     473             :         ResourceElem *newhash;
     474             : 
     475       13160 :         oldhash = owner->hash;
     476       13160 :         oldcap = owner->capacity;
     477             : 
     478             :         /* Double the capacity (it must stay a power of 2!) */
     479       13160 :         newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE;
     480       13160 :         newhash = (ResourceElem *) MemoryContextAllocZero(TopMemoryContext,
     481             :                                                           newcap * sizeof(ResourceElem));
     482             : 
     483             :         /*
     484             :          * We assume we can't fail below this point, so OK to scribble on the
     485             :          * owner
     486             :          */
     487       13160 :         owner->hash = newhash;
     488       13160 :         owner->capacity = newcap;
     489       13160 :         owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap);
     490       13160 :         owner->nhash = 0;
     491             : 
     492       13160 :         if (oldhash != NULL)
     493             :         {
     494             :             /*
     495             :              * Transfer any pre-existing entries into the new hash table; they
     496             :              * don't necessarily go where they were before, so this simple
     497             :              * logic is the best way.
     498             :              */
     499     2175464 :             for (i = 0; i < oldcap; i++)
     500             :             {
     501     2171264 :                 if (oldhash[i].kind != NULL)
     502     1496478 :                     ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
     503             :             }
     504             : 
     505             :             /* And release old hash table. */
     506        4200 :             pfree(oldhash);
     507             :         }
     508             :     }
     509             : 
     510             :     /* Move items from the array to the hash */
     511     1363626 :     for (int i = 0; i < owner->narr; i++)
     512     1322304 :         ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
     513       41322 :     owner->narr = 0;
     514             : 
     515             :     Assert(owner->nhash <= owner->grow_at);
     516             : }
     517             : 
     518             : /*
     519             :  * Remember that an object is owned by a ResourceOwner
     520             :  *
     521             :  * Caller must have previously done ResourceOwnerEnlarge()
     522             :  */
     523             : void
     524   337817548 : ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
     525             : {
     526             :     uint32      idx;
     527             : 
     528             :     /* sanity check the ResourceOwnerDesc */
     529             :     Assert(kind->release_phase != 0);
     530             :     Assert(kind->release_priority != 0);
     531             : 
     532             :     /*
     533             :      * Mustn't try to remember more resources after we have already started
     534             :      * releasing.  We already checked this in ResourceOwnerEnlarge.
     535             :      */
     536             :     Assert(!owner->releasing);
     537             :     Assert(!owner->sorted);
     538             : 
     539   337817548 :     if (owner->narr >= RESOWNER_ARRAY_SIZE)
     540             :     {
     541             :         /* forgot to call ResourceOwnerEnlarge? */
     542           0 :         elog(ERROR, "ResourceOwnerRemember called but array was full");
     543             :     }
     544             : 
     545             :     /* Append to the array. */
     546   337817548 :     idx = owner->narr;
     547   337817548 :     owner->arr[idx].item = value;
     548   337817548 :     owner->arr[idx].kind = kind;
     549   337817548 :     owner->narr++;
     550   337817548 : }
     551             : 
     552             : /*
     553             :  * Forget that an object is owned by a ResourceOwner
     554             :  *
     555             :  * Note: If same resource ID is associated with the ResourceOwner more than
     556             :  * once, one instance is removed.
     557             :  *
     558             :  * Note: Forgetting a resource does not guarantee that there is room to
     559             :  * remember a new resource.  One exception is when you forget the most
     560             :  * recently remembered resource; that does make room for a new remember call.
     561             :  * Some code callers rely on that exception.
     562             :  */
     563             : void
     564   337195578 : ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
     565             : {
     566             :     /*
     567             :      * Mustn't call this after we have already started releasing resources.
     568             :      * (Release callback functions are not allowed to release additional
     569             :      * resources.)
     570             :      */
     571   337195578 :     if (owner->releasing)
     572           2 :         elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
     573             :     Assert(!owner->sorted);
     574             : 
     575             :     /* Search through all items in the array first. */
     576   443206188 :     for (int i = owner->narr - 1; i >= 0; i--)
     577             :     {
     578   442317128 :         if (owner->arr[i].item == value &&
     579   336307068 :             owner->arr[i].kind == kind)
     580             :         {
     581   336306516 :             owner->arr[i] = owner->arr[owner->narr - 1];
     582   336306516 :             owner->narr--;
     583             : 
     584             : #ifdef RESOWNER_STATS
     585             :             narray_lookups++;
     586             : #endif
     587   336306516 :             return;
     588             :         }
     589             :     }
     590             : 
     591             :     /* Search hash */
     592      889060 :     if (owner->nhash > 0)
     593             :     {
     594      889060 :         uint32      mask = owner->capacity - 1;
     595             :         uint32      idx;
     596             : 
     597      889060 :         idx = hash_resource_elem(value, kind) & mask;
     598   199192640 :         for (uint32 i = 0; i < owner->capacity; i++)
     599             :         {
     600   199192640 :             if (owner->hash[idx].item == value &&
     601      893456 :                 owner->hash[idx].kind == kind)
     602             :             {
     603      889060 :                 owner->hash[idx].item = (Datum) 0;
     604      889060 :                 owner->hash[idx].kind = NULL;
     605      889060 :                 owner->nhash--;
     606             : 
     607             : #ifdef RESOWNER_STATS
     608             :                 nhash_lookups++;
     609             : #endif
     610      889060 :                 return;
     611             :             }
     612   198303580 :             idx = (idx + 1) & mask;
     613             :         }
     614             :     }
     615             : 
     616             :     /*
     617             :      * Use %p to print the reference, since most objects tracked by a resource
     618             :      * owner are pointers.  It's a bit misleading if it's not a pointer, but
     619             :      * this is a programmer error, anyway.
     620             :      */
     621           0 :     elog(ERROR, "%s %p is not owned by resource owner %s",
     622             :          kind->name, DatumGetPointer(value), owner->name);
     623             : }
     624             : 
     625             : /*
     626             :  * ResourceOwnerRelease
     627             :  *      Release all resources owned by a ResourceOwner and its descendants,
     628             :  *      but don't delete the owner objects themselves.
     629             :  *
     630             :  * Note that this executes just one phase of release, and so typically
     631             :  * must be called three times.  We do it this way because (a) we want to
     632             :  * do all the recursion separately for each phase, thereby preserving
     633             :  * the needed order of operations; and (b) xact.c may have other operations
     634             :  * to do between the phases.
     635             :  *
     636             :  * phase: release phase to execute
     637             :  * isCommit: true for successful completion of a query or transaction,
     638             :  *          false for unsuccessful
     639             :  * isTopLevel: true if completing a main transaction, else false
     640             :  *
     641             :  * isCommit is passed because some modules may expect that their resources
     642             :  * were all released already if the transaction or portal finished normally.
     643             :  * If so it is reasonable to give a warning (NOT an error) should any
     644             :  * unreleased resources be present.  When isCommit is false, such warnings
     645             :  * are generally inappropriate.
     646             :  *
     647             :  * isTopLevel is passed when we are releasing TopTransactionResourceOwner
     648             :  * at completion of a main transaction.  This generally means that *all*
     649             :  * resources will be released, and so we can optimize things a bit.
     650             :  *
     651             :  * NOTE: After starting the release process, by calling this function, no new
     652             :  * resources can be remembered in the resource owner.  You also cannot call
     653             :  * ResourceOwnerForget on any previously remembered resources to release
     654             :  * resources "in retail" after that, you must let the bulk release take care
     655             :  * of them.
     656             :  */
     657             : void
     658     4612198 : ResourceOwnerRelease(ResourceOwner owner,
     659             :                      ResourceReleasePhase phase,
     660             :                      bool isCommit,
     661             :                      bool isTopLevel)
     662             : {
     663             :     /* There's not currently any setup needed before recursing */
     664     4612198 :     ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
     665             : 
     666             : #ifdef RESOWNER_STATS
     667             :     if (isTopLevel)
     668             :     {
     669             :         elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d",
     670             :              narray_lookups, nhash_lookups);
     671             :         narray_lookups = 0;
     672             :         nhash_lookups = 0;
     673             :     }
     674             : #endif
     675     4612198 : }
     676             : 
     677             : static void
     678     4818634 : ResourceOwnerReleaseInternal(ResourceOwner owner,
     679             :                              ResourceReleasePhase phase,
     680             :                              bool isCommit,
     681             :                              bool isTopLevel)
     682             : {
     683             :     ResourceOwner child;
     684             :     ResourceOwner save;
     685             :     ResourceReleaseCallbackItem *item;
     686             :     ResourceReleaseCallbackItem *next;
     687             : 
     688             :     /* Recurse to handle descendants */
     689     5025070 :     for (child = owner->firstchild; child != NULL; child = child->nextchild)
     690      206436 :         ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
     691             : 
     692             :     /*
     693             :      * To release the resources in the right order, sort them by phase and
     694             :      * priority.
     695             :      *
     696             :      * The ReleaseResource callback functions are not allowed to remember or
     697             :      * forget any other resources after this. Otherwise we lose track of where
     698             :      * we are in processing the hash/array.
     699             :      */
     700     4818634 :     if (!owner->releasing)
     701             :     {
     702             :         Assert(phase == RESOURCE_RELEASE_BEFORE_LOCKS);
     703             :         Assert(!owner->sorted);
     704     1606210 :         owner->releasing = true;
     705             :     }
     706             :     else
     707             :     {
     708             :         /*
     709             :          * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not
     710             :          * the first call to ResourceOwnerRelease. But if an error happens
     711             :          * between the release phases, we might get called again for the same
     712             :          * ResourceOwner from AbortTransaction.
     713             :          */
     714             :     }
     715     4818634 :     if (!owner->sorted)
     716             :     {
     717     1606210 :         ResourceOwnerSort(owner);
     718     1606210 :         owner->sorted = true;
     719             :     }
     720             : 
     721             :     /*
     722             :      * Make CurrentResourceOwner point to me, so that the release callback
     723             :      * functions know which resource owner is been released.
     724             :      */
     725     4818634 :     save = CurrentResourceOwner;
     726     4818634 :     CurrentResourceOwner = owner;
     727             : 
     728     4818634 :     if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
     729             :     {
     730             :         /*
     731             :          * Release all resources that need to be released before the locks.
     732             :          *
     733             :          * During a commit, there shouldn't be any remaining resources ---
     734             :          * that would indicate failure to clean up the executor correctly ---
     735             :          * so issue warnings.  In the abort case, just clean up quietly.
     736             :          */
     737     1606214 :         ResourceOwnerReleaseAll(owner, phase, isCommit);
     738             : 
     739     1606248 :         while (!dlist_is_empty(&owner->aio_handles))
     740             :         {
     741          34 :             dlist_node *node = dlist_head_node(&owner->aio_handles);
     742             : 
     743          34 :             pgaio_io_release_resowner(node, !isCommit);
     744             :         }
     745             :     }
     746     3212420 :     else if (phase == RESOURCE_RELEASE_LOCKS)
     747             :     {
     748     1606210 :         if (isTopLevel)
     749             :         {
     750             :             /*
     751             :              * For a top-level xact we are going to release all locks (or at
     752             :              * least all non-session locks), so just do a single lmgr call at
     753             :              * the top of the recursion.
     754             :              */
     755      897146 :             if (owner == TopTransactionResourceOwner)
     756             :             {
     757      818766 :                 ProcReleaseLocks(isCommit);
     758      818766 :                 ReleasePredicateLocks(isCommit, false);
     759             :             }
     760             :         }
     761             :         else
     762             :         {
     763             :             /*
     764             :              * Release locks retail.  Note that if we are committing a
     765             :              * subtransaction, we do NOT release its locks yet, but transfer
     766             :              * them to the parent.
     767             :              */
     768             :             LOCALLOCK **locks;
     769             :             int         nlocks;
     770             : 
     771             :             Assert(owner->parent != NULL);
     772             : 
     773             :             /*
     774             :              * Pass the list of locks owned by this resource owner to the lock
     775             :              * manager, unless it has overflowed.
     776             :              */
     777      709064 :             if (owner->nlocks > MAX_RESOWNER_LOCKS)
     778             :             {
     779        6910 :                 locks = NULL;
     780        6910 :                 nlocks = 0;
     781             :             }
     782             :             else
     783             :             {
     784      702154 :                 locks = owner->locks;
     785      702154 :                 nlocks = owner->nlocks;
     786             :             }
     787             : 
     788      709064 :             if (isCommit)
     789      698432 :                 LockReassignCurrentOwner(locks, nlocks);
     790             :             else
     791       10632 :                 LockReleaseCurrentOwner(locks, nlocks);
     792             :         }
     793             :     }
     794     1606210 :     else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
     795             :     {
     796             :         /*
     797             :          * Release all resources that need to be released after the locks.
     798             :          */
     799     1606210 :         ResourceOwnerReleaseAll(owner, phase, isCommit);
     800             :     }
     801             : 
     802             :     /* Let add-on modules get a chance too */
     803     4818634 :     for (item = ResourceRelease_callbacks; item; item = next)
     804             :     {
     805             :         /* allow callbacks to unregister themselves when called */
     806           0 :         next = item->next;
     807           0 :         item->callback(phase, isCommit, isTopLevel, item->arg);
     808             :     }
     809             : 
     810     4818634 :     CurrentResourceOwner = save;
     811     4818634 : }
     812             : 
     813             : /*
     814             :  * ResourceOwnerReleaseAllOfKind
     815             :  *      Release all resources of a certain type held by this owner.
     816             :  */
     817             : void
     818       16394 : ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
     819             : {
     820             :     /* Mustn't call this after we have already started releasing resources. */
     821       16394 :     if (owner->releasing)
     822           0 :         elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
     823             :     Assert(!owner->sorted);
     824             : 
     825             :     /*
     826             :      * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember
     827             :      * while we're scanning the owner.  Enlarging the hash would cause us to
     828             :      * lose track of the point we're scanning.
     829             :      */
     830       16394 :     owner->releasing = true;
     831             : 
     832             :     /* Array first */
     833       71446 :     for (int i = 0; i < owner->narr; i++)
     834             :     {
     835       55052 :         if (owner->arr[i].kind == kind)
     836             :         {
     837       55052 :             Datum       value = owner->arr[i].item;
     838             : 
     839       55052 :             owner->arr[i] = owner->arr[owner->narr - 1];
     840       55052 :             owner->narr--;
     841       55052 :             i--;
     842             : 
     843       55052 :             kind->ReleaseResource(value);
     844             :         }
     845             :     }
     846             : 
     847             :     /* Then hash */
     848       50058 :     for (int i = 0; i < owner->capacity; i++)
     849             :     {
     850       33664 :         if (owner->hash[i].kind == kind)
     851             :         {
     852       16832 :             Datum       value = owner->hash[i].item;
     853             : 
     854       16832 :             owner->hash[i].item = (Datum) 0;
     855       16832 :             owner->hash[i].kind = NULL;
     856       16832 :             owner->nhash--;
     857             : 
     858       16832 :             kind->ReleaseResource(value);
     859             :         }
     860             :     }
     861       16394 :     owner->releasing = false;
     862       16394 : }
     863             : 
     864             : /*
     865             :  * ResourceOwnerDelete
     866             :  *      Delete an owner object and its descendants.
     867             :  *
     868             :  * The caller must have already released all resources in the object tree.
     869             :  */
     870             : void
     871     1596820 : ResourceOwnerDelete(ResourceOwner owner)
     872             : {
     873             :     /* We had better not be deleting CurrentResourceOwner ... */
     874             :     Assert(owner != CurrentResourceOwner);
     875             : 
     876             :     /* And it better not own any resources, either */
     877             :     Assert(owner->narr == 0);
     878             :     Assert(owner->nhash == 0);
     879             :     Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
     880             : 
     881             :     /*
     882             :      * Delete children.  The recursive call will delink the child from me, so
     883             :      * just iterate as long as there is a child.
     884             :      */
     885     1665638 :     while (owner->firstchild != NULL)
     886       68818 :         ResourceOwnerDelete(owner->firstchild);
     887             : 
     888             :     /*
     889             :      * We delink the owner from its parent before deleting it, so that if
     890             :      * there's an error we won't have deleted/busted owners still attached to
     891             :      * the owner tree.  Better a leak than a crash.
     892             :      */
     893     1596820 :     ResourceOwnerNewParent(owner, NULL);
     894             : 
     895             :     /* And free the object. */
     896     1596820 :     if (owner->hash)
     897        8960 :         pfree(owner->hash);
     898     1596820 :     pfree(owner);
     899     1596820 : }
     900             : 
     901             : /*
     902             :  * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
     903             :  */
     904             : ResourceOwner
     905      698432 : ResourceOwnerGetParent(ResourceOwner owner)
     906             : {
     907      698432 :     return owner->parent;
     908             : }
     909             : 
     910             : /*
     911             :  * Reassign a ResourceOwner to have a new parent
     912             :  */
     913             : void
     914     1596894 : ResourceOwnerNewParent(ResourceOwner owner,
     915             :                        ResourceOwner newparent)
     916             : {
     917     1596894 :     ResourceOwner oldparent = owner->parent;
     918             : 
     919     1596894 :     if (oldparent)
     920             :     {
     921      776642 :         if (owner == oldparent->firstchild)
     922      761156 :             oldparent->firstchild = owner->nextchild;
     923             :         else
     924             :         {
     925             :             ResourceOwner child;
     926             : 
     927       17966 :             for (child = oldparent->firstchild; child; child = child->nextchild)
     928             :             {
     929       17966 :                 if (owner == child->nextchild)
     930             :                 {
     931       15486 :                     child->nextchild = owner->nextchild;
     932       15486 :                     break;
     933             :                 }
     934             :             }
     935             :         }
     936             :     }
     937             : 
     938     1596894 :     if (newparent)
     939             :     {
     940             :         Assert(owner != newparent);
     941          74 :         owner->parent = newparent;
     942          74 :         owner->nextchild = newparent->firstchild;
     943          74 :         newparent->firstchild = owner;
     944             :     }
     945             :     else
     946             :     {
     947     1596820 :         owner->parent = NULL;
     948     1596820 :         owner->nextchild = NULL;
     949             :     }
     950     1596894 : }
     951             : 
     952             : /*
     953             :  * Register or deregister callback functions for resource cleanup
     954             :  *
     955             :  * These functions can be used by dynamically loaded modules.  These used
     956             :  * to be the only way for an extension to register custom resource types
     957             :  * with a resource owner, but nowadays it is easier to define a new
     958             :  * ResourceOwnerDesc with custom callbacks.
     959             :  */
     960             : void
     961           0 : RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
     962             : {
     963             :     ResourceReleaseCallbackItem *item;
     964             : 
     965             :     item = (ResourceReleaseCallbackItem *)
     966           0 :         MemoryContextAlloc(TopMemoryContext,
     967             :                            sizeof(ResourceReleaseCallbackItem));
     968           0 :     item->callback = callback;
     969           0 :     item->arg = arg;
     970           0 :     item->next = ResourceRelease_callbacks;
     971           0 :     ResourceRelease_callbacks = item;
     972           0 : }
     973             : 
     974             : void
     975           0 : UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
     976             : {
     977             :     ResourceReleaseCallbackItem *item;
     978             :     ResourceReleaseCallbackItem *prev;
     979             : 
     980           0 :     prev = NULL;
     981           0 :     for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
     982             :     {
     983           0 :         if (item->callback == callback && item->arg == arg)
     984             :         {
     985           0 :             if (prev)
     986           0 :                 prev->next = item->next;
     987             :             else
     988           0 :                 ResourceRelease_callbacks = item->next;
     989           0 :             pfree(item);
     990           0 :             break;
     991             :         }
     992             :     }
     993           0 : }
     994             : 
     995             : /*
     996             :  * Establish an AuxProcessResourceOwner for the current process.
     997             :  */
     998             : void
     999       10262 : CreateAuxProcessResourceOwner(void)
    1000             : {
    1001             :     Assert(AuxProcessResourceOwner == NULL);
    1002             :     Assert(CurrentResourceOwner == NULL);
    1003       10262 :     AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
    1004       10262 :     CurrentResourceOwner = AuxProcessResourceOwner;
    1005             : 
    1006             :     /*
    1007             :      * Register a shmem-exit callback for cleanup of aux-process resource
    1008             :      * owner.  (This needs to run after, e.g., ShutdownXLOG.)
    1009             :      */
    1010       10262 :     on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
    1011       10262 : }
    1012             : 
    1013             : /*
    1014             :  * Convenience routine to release all resources tracked in
    1015             :  * AuxProcessResourceOwner (but that resowner is not destroyed here).
    1016             :  * Warn about leaked resources if isCommit is true.
    1017             :  */
    1018             : void
    1019       10882 : ReleaseAuxProcessResources(bool isCommit)
    1020             : {
    1021             :     /*
    1022             :      * At this writing, the only thing that could actually get released is
    1023             :      * buffer pins; but we may as well do the full release protocol.
    1024             :      */
    1025       10882 :     ResourceOwnerRelease(AuxProcessResourceOwner,
    1026             :                          RESOURCE_RELEASE_BEFORE_LOCKS,
    1027             :                          isCommit, true);
    1028       10882 :     ResourceOwnerRelease(AuxProcessResourceOwner,
    1029             :                          RESOURCE_RELEASE_LOCKS,
    1030             :                          isCommit, true);
    1031       10882 :     ResourceOwnerRelease(AuxProcessResourceOwner,
    1032             :                          RESOURCE_RELEASE_AFTER_LOCKS,
    1033             :                          isCommit, true);
    1034             :     /* allow it to be reused */
    1035       10882 :     AuxProcessResourceOwner->releasing = false;
    1036       10882 :     AuxProcessResourceOwner->sorted = false;
    1037       10882 : }
    1038             : 
    1039             : /*
    1040             :  * Shmem-exit callback for the same.
    1041             :  * Warn about leaked resources if process exit code is zero (ie normal).
    1042             :  */
    1043             : static void
    1044       10262 : ReleaseAuxProcessResourcesCallback(int code, Datum arg)
    1045             : {
    1046       10262 :     bool        isCommit = (code == 0);
    1047             : 
    1048       10262 :     ReleaseAuxProcessResources(isCommit);
    1049       10262 : }
    1050             : 
    1051             : /*
    1052             :  * Remember that a Local Lock is owned by a ResourceOwner
    1053             :  *
    1054             :  * This is different from the generic ResourceOwnerRemember in that the list of
    1055             :  * locks is only a lossy cache.  It can hold up to MAX_RESOWNER_LOCKS entries,
    1056             :  * and when it overflows, we stop tracking locks.  The point of only remembering
    1057             :  * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
    1058             :  * ResourceOwnerForgetLock doesn't need to scan through a large array to find
    1059             :  * the entry.
    1060             :  */
    1061             : void
    1062    35585044 : ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
    1063             : {
    1064             :     Assert(locallock != NULL);
    1065             : 
    1066    35585044 :     if (owner->nlocks > MAX_RESOWNER_LOCKS)
    1067     3878302 :         return;                 /* we have already overflowed */
    1068             : 
    1069    31706742 :     if (owner->nlocks < MAX_RESOWNER_LOCKS)
    1070    31687696 :         owner->locks[owner->nlocks] = locallock;
    1071             :     else
    1072             :     {
    1073             :         /* overflowed */
    1074             :     }
    1075    31706742 :     owner->nlocks++;
    1076             : }
    1077             : 
    1078             : /*
    1079             :  * Forget that a Local Lock is owned by a ResourceOwner
    1080             :  */
    1081             : void
    1082    35585044 : ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
    1083             : {
    1084             :     int         i;
    1085             : 
    1086    35585044 :     if (owner->nlocks > MAX_RESOWNER_LOCKS)
    1087     4183038 :         return;                 /* we have overflowed */
    1088             : 
    1089             :     Assert(owner->nlocks > 0);
    1090    35425464 :     for (i = owner->nlocks - 1; i >= 0; i--)
    1091             :     {
    1092    35425464 :         if (locallock == owner->locks[i])
    1093             :         {
    1094    31402006 :             owner->locks[i] = owner->locks[owner->nlocks - 1];
    1095    31402006 :             owner->nlocks--;
    1096    31402006 :             return;
    1097             :         }
    1098             :     }
    1099           0 :     elog(ERROR, "lock reference %p is not owned by resource owner %s",
    1100             :          locallock, owner->name);
    1101             : }
    1102             : 
    1103             : void
    1104     2436110 : ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
    1105             : {
    1106     2436110 :     dlist_push_tail(&owner->aio_handles, ioh_node);
    1107     2436110 : }
    1108             : 
    1109             : void
    1110     2436110 : ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
    1111             : {
    1112     2436110 :     dlist_delete_from(&owner->aio_handles, ioh_node);
    1113     2436110 : }

Generated by: LCOV version 1.14