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

Generated by: LCOV version 2.0-1