LCOV - code coverage report
Current view: top level - src/backend/utils/resowner - resowner.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 91.3 % 263 240
Test Date: 2026-07-06 22:15:47 Functions: 91.7 % 24 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 79.9 % 144 115

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

Generated by: LCOV version 2.0-1