LCOV - code coverage report
Current view: top level - src/backend/utils/resowner - resowner.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.3 % 360 318
Test Date: 2026-01-26 10:56:24 Functions: 91.7 % 24 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 63.6 % 225 143

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

Generated by: LCOV version 2.3.2-1