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

Generated by: LCOV version 1.14