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

Generated by: LCOV version 2.0-1