LCOV - code coverage report
Current view: top level - src/test/modules/test_resowner - test_resowner_many.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.1 % 111 100
Test Date: 2026-09-20 12:15:49 Functions: 87.5 % 8 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 47.2 % 72 34

             Branch data     Line data    Source code
       1                 :             : /*--------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * test_resowner_many.c
       4                 :             :  *      Test ResourceOwner functionality with lots of resources
       5                 :             :  *
       6                 :             :  * Copyright (c) 2022-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *      src/test/modules/test_resowner/test_resowner_many.c
      10                 :             :  *
      11                 :             :  * -------------------------------------------------------------------------
      12                 :             :  */
      13                 :             : #include "postgres.h"
      14                 :             : 
      15                 :             : #include "fmgr.h"
      16                 :             : #include "lib/ilist.h"
      17                 :             : #include "utils/resowner.h"
      18                 :             : 
      19                 :             : /*
      20                 :             :  * Define a custom resource type to use in the test.  The resource being
      21                 :             :  * tracked is a palloc'd ManyTestResource struct.
      22                 :             :  *
      23                 :             :  * To cross-check that the ResourceOwner calls the callback functions
      24                 :             :  * correctly, we keep track of the remembered resources ourselves in a linked
      25                 :             :  * list, and also keep counters of how many times the callback functions have
      26                 :             :  * been called.
      27                 :             :  */
      28                 :             : typedef struct
      29                 :             : {
      30                 :             :     ResourceOwnerDesc desc;
      31                 :             :     int         nremembered;
      32                 :             :     int         nforgotten;
      33                 :             :     int         nreleased;
      34                 :             :     int         nleaked;
      35                 :             : 
      36                 :             :     dlist_head  current_resources;
      37                 :             : } ManyTestResourceKind;
      38                 :             : 
      39                 :             : typedef struct
      40                 :             : {
      41                 :             :     ManyTestResourceKind *kind;
      42                 :             :     dlist_node  node;
      43                 :             : } ManyTestResource;
      44                 :             : 
      45                 :             : /*
      46                 :             :  * Priority of last call to the release callback.
      47                 :             :  * This is used to check that the resources are released in correct order.
      48                 :             :  */
      49                 :             : static uint32 last_release_priority = 0;
      50                 :             : 
      51                 :             : /* prototypes for local functions */
      52                 :             : static void ReleaseManyTestResource(Datum res);
      53                 :             : static char *PrintManyTest(Datum res);
      54                 :             : static void InitManyTestResourceKind(ManyTestResourceKind *kind, char *name,
      55                 :             :                                      ResourceReleasePhase phase, uint32 priority);
      56                 :             : static void RememberManyTestResources(ResourceOwner owner,
      57                 :             :                                       ManyTestResourceKind *kinds, int nkinds,
      58                 :             :                                       int nresources);
      59                 :             : static void ForgetManyTestResources(ResourceOwner owner,
      60                 :             :                                     ManyTestResourceKind *kinds, int nkinds,
      61                 :             :                                     int nresources);
      62                 :             : static int  GetTotalResourceCount(ManyTestResourceKind *kinds, int nkinds);
      63                 :             : 
      64                 :             : /* ResourceOwner callback */
      65                 :             : static void
      66                 :      199000 : ReleaseManyTestResource(Datum res)
      67                 :             : {
      68                 :      199000 :     ManyTestResource *mres = (ManyTestResource *) DatumGetPointer(res);
      69                 :             : 
      70         [ -  + ]:      199000 :     elog(DEBUG1, "releasing resource %p from %s", mres, mres->kind->desc.name);
      71                 :             :     Assert(last_release_priority <= mres->kind->desc.release_priority);
      72                 :             : 
      73                 :      199000 :     dlist_delete(&mres->node);
      74                 :      199000 :     mres->kind->nreleased++;
      75                 :      199000 :     last_release_priority = mres->kind->desc.release_priority;
      76                 :      199000 :     pfree(mres);
      77                 :      199000 : }
      78                 :             : 
      79                 :             : /* ResourceOwner callback */
      80                 :             : static char *
      81                 :           0 : PrintManyTest(Datum res)
      82                 :             : {
      83                 :           0 :     ManyTestResource *mres = (ManyTestResource *) DatumGetPointer(res);
      84                 :             : 
      85                 :             :     /*
      86                 :             :      * XXX: we assume that the DebugPrint function is called once for each
      87                 :             :      * leaked resource, and that there are no other callers.
      88                 :             :      */
      89                 :           0 :     mres->kind->nleaked++;
      90                 :             : 
      91                 :           0 :     return psprintf("many-test resource from %s", mres->kind->desc.name);
      92                 :             : }
      93                 :             : 
      94                 :             : static void
      95                 :           6 : InitManyTestResourceKind(ManyTestResourceKind *kind, char *name,
      96                 :             :                          ResourceReleasePhase phase, uint32 priority)
      97                 :             : {
      98                 :           6 :     kind->desc.name = name;
      99                 :           6 :     kind->desc.release_phase = phase;
     100                 :           6 :     kind->desc.release_priority = priority;
     101                 :           6 :     kind->desc.ReleaseResource = ReleaseManyTestResource;
     102                 :           6 :     kind->desc.DebugPrint = PrintManyTest;
     103                 :           6 :     kind->nremembered = 0;
     104                 :           6 :     kind->nforgotten = 0;
     105                 :           6 :     kind->nreleased = 0;
     106                 :           6 :     kind->nleaked = 0;
     107                 :           6 :     dlist_init(&kind->current_resources);
     108                 :           6 : }
     109                 :             : 
     110                 :             : /*
     111                 :             :  * Remember 'nresources' resources.  The resources are remembered in round
     112                 :             :  * robin fashion with the kinds from 'kinds' array.
     113                 :             :  */
     114                 :             : static void
     115                 :           2 : RememberManyTestResources(ResourceOwner owner,
     116                 :             :                           ManyTestResourceKind *kinds, int nkinds,
     117                 :             :                           int nresources)
     118                 :             : {
     119                 :           2 :     int         kind_idx = 0;
     120                 :             : 
     121         [ +  + ]:      200002 :     for (int i = 0; i < nresources; i++)
     122                 :             :     {
     123                 :      200000 :         ManyTestResource *mres = palloc_object(ManyTestResource);
     124                 :             : 
     125                 :      200000 :         mres->kind = &kinds[kind_idx];
     126                 :      200000 :         dlist_node_init(&mres->node);
     127                 :             : 
     128                 :      200000 :         ResourceOwnerEnlarge(owner);
     129                 :      200000 :         ResourceOwnerRemember(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
     130                 :      200000 :         kinds[kind_idx].nremembered++;
     131                 :      200000 :         dlist_push_tail(&kinds[kind_idx].current_resources, &mres->node);
     132                 :             : 
     133         [ -  + ]:      200000 :         elog(DEBUG1, "remembered resource %p from %s", mres, mres->kind->desc.name);
     134                 :             : 
     135                 :      200000 :         kind_idx = (kind_idx + 1) % nkinds;
     136                 :             :     }
     137                 :           2 : }
     138                 :             : 
     139                 :             : /*
     140                 :             :  * Forget 'nresources' resources, in round robin fashion from 'kinds'.
     141                 :             :  */
     142                 :             : static void
     143                 :           2 : ForgetManyTestResources(ResourceOwner owner,
     144                 :             :                         ManyTestResourceKind *kinds, int nkinds,
     145                 :             :                         int nresources)
     146                 :             : {
     147                 :           2 :     int         kind_idx = 0;
     148                 :             :     int         ntotal;
     149                 :             : 
     150                 :           2 :     ntotal = GetTotalResourceCount(kinds, nkinds);
     151         [ -  + ]:           2 :     if (ntotal < nresources)
     152         [ #  # ]:           0 :         elog(PANIC, "cannot free %d resources, only %d remembered", nresources, ntotal);
     153                 :             : 
     154         [ +  + ]:        1002 :     for (int i = 0; i < nresources; i++)
     155                 :             :     {
     156                 :        1000 :         bool        found = false;
     157                 :             : 
     158         [ +  - ]:        1000 :         for (int j = 0; j < nkinds; j++)
     159                 :             :         {
     160                 :        1000 :             kind_idx = (kind_idx + 1) % nkinds;
     161         [ +  - ]:        1000 :             if (!dlist_is_empty(&kinds[kind_idx].current_resources))
     162                 :             :             {
     163                 :        1000 :                 ManyTestResource *mres = dlist_head_element(ManyTestResource, node, &kinds[kind_idx].current_resources);
     164                 :             : 
     165                 :        1000 :                 ResourceOwnerForget(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
     166                 :        1000 :                 kinds[kind_idx].nforgotten++;
     167                 :        1000 :                 dlist_delete(&mres->node);
     168                 :        1000 :                 pfree(mres);
     169                 :             : 
     170                 :        1000 :                 found = true;
     171                 :        1000 :                 break;
     172                 :             :             }
     173                 :             :         }
     174         [ -  + ]:        1000 :         if (!found)
     175         [ #  # ]:           0 :             elog(ERROR, "could not find a test resource to forget");
     176                 :             :     }
     177                 :           2 : }
     178                 :             : 
     179                 :             : /*
     180                 :             :  * Get total number of currently active resources among 'kinds'.
     181                 :             :  */
     182                 :             : static int
     183                 :           2 : GetTotalResourceCount(ManyTestResourceKind *kinds, int nkinds)
     184                 :             : {
     185                 :           2 :     int         ntotal = 0;
     186                 :             : 
     187         [ +  + ]:           8 :     for (int i = 0; i < nkinds; i++)
     188                 :           6 :         ntotal += kinds[i].nremembered - kinds[i].nforgotten - kinds[i].nreleased;
     189                 :             : 
     190                 :           2 :     return ntotal;
     191                 :             : }
     192                 :             : 
     193                 :             : /*
     194                 :             :  * Remember lots of resources, belonging to 'nkinds' different resource types
     195                 :             :  * with different priorities.  Then forget some of them, and finally, release
     196                 :             :  * the resource owner.  We use a custom resource type that performs various
     197                 :             :  * sanity checks to verify that all the resources are released, and in the
     198                 :             :  * correct order.
     199                 :             :  */
     200                 :           2 : PG_FUNCTION_INFO_V1(test_resowner_many);
     201                 :             : Datum
     202                 :           1 : test_resowner_many(PG_FUNCTION_ARGS)
     203                 :             : {
     204                 :           1 :     int32       nkinds = PG_GETARG_INT32(0);
     205                 :           1 :     int32       nremember_bl = PG_GETARG_INT32(1);
     206                 :           1 :     int32       nforget_bl = PG_GETARG_INT32(2);
     207                 :           1 :     int32       nremember_al = PG_GETARG_INT32(3);
     208                 :           1 :     int32       nforget_al = PG_GETARG_INT32(4);
     209                 :             : 
     210                 :             :     ResourceOwner resowner;
     211                 :             : 
     212                 :             :     ManyTestResourceKind *before_kinds;
     213                 :             :     ManyTestResourceKind *after_kinds;
     214                 :             : 
     215                 :             :     /* Sanity check the arguments */
     216         [ -  + ]:           1 :     if (nkinds < 0)
     217         [ #  # ]:           0 :         elog(ERROR, "nkinds must be >= 0");
     218         [ -  + ]:           1 :     if (nremember_bl < 0)
     219         [ #  # ]:           0 :         elog(ERROR, "nremember_bl must be >= 0");
     220   [ +  -  -  + ]:           1 :     if (nforget_bl < 0 || nforget_bl > nremember_bl)
     221         [ #  # ]:           0 :         elog(ERROR, "nforget_bl must between 0 and 'nremember_bl'");
     222         [ -  + ]:           1 :     if (nremember_al < 0)
     223         [ #  # ]:           0 :         elog(ERROR, "nremember_al must be greater than zero");
     224   [ +  -  -  + ]:           1 :     if (nforget_al < 0 || nforget_al > nremember_al)
     225         [ #  # ]:           0 :         elog(ERROR, "nforget_al must between 0 and 'nremember_al'");
     226                 :             : 
     227                 :             :     /* Initialize all the different resource kinds to use */
     228                 :           1 :     before_kinds = palloc_array(ManyTestResourceKind, nkinds);
     229         [ +  + ]:           4 :     for (int i = 0; i < nkinds; i++)
     230                 :             :     {
     231                 :           3 :         InitManyTestResourceKind(&before_kinds[i],
     232                 :             :                                  psprintf("resource before locks %d", i),
     233                 :             :                                  RESOURCE_RELEASE_BEFORE_LOCKS,
     234                 :           3 :                                  RELEASE_PRIO_FIRST + i);
     235                 :             :     }
     236                 :           1 :     after_kinds = palloc_array(ManyTestResourceKind, nkinds);
     237         [ +  + ]:           4 :     for (int i = 0; i < nkinds; i++)
     238                 :             :     {
     239                 :           3 :         InitManyTestResourceKind(&after_kinds[i],
     240                 :             :                                  psprintf("resource after locks %d", i),
     241                 :             :                                  RESOURCE_RELEASE_AFTER_LOCKS,
     242                 :           3 :                                  RELEASE_PRIO_FIRST + i);
     243                 :             :     }
     244                 :             : 
     245                 :           1 :     resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
     246                 :             : 
     247                 :             :     /* Remember a bunch of resources */
     248         [ +  - ]:           1 :     if (nremember_bl > 0)
     249                 :             :     {
     250         [ +  - ]:           1 :         elog(NOTICE, "remembering %d before-locks resources", nremember_bl);
     251                 :           1 :         RememberManyTestResources(resowner, before_kinds, nkinds, nremember_bl);
     252                 :             :     }
     253         [ +  - ]:           1 :     if (nremember_al > 0)
     254                 :             :     {
     255         [ +  - ]:           1 :         elog(NOTICE, "remembering %d after-locks resources", nremember_al);
     256                 :           1 :         RememberManyTestResources(resowner, after_kinds, nkinds, nremember_al);
     257                 :             :     }
     258                 :             : 
     259                 :             :     /* Forget what was remembered */
     260         [ +  - ]:           1 :     if (nforget_bl > 0)
     261                 :             :     {
     262         [ +  - ]:           1 :         elog(NOTICE, "forgetting %d before-locks resources", nforget_bl);
     263                 :           1 :         ForgetManyTestResources(resowner, before_kinds, nkinds, nforget_bl);
     264                 :             :     }
     265                 :             : 
     266         [ +  - ]:           1 :     if (nforget_al > 0)
     267                 :             :     {
     268         [ +  - ]:           1 :         elog(NOTICE, "forgetting %d after-locks resources", nforget_al);
     269                 :           1 :         ForgetManyTestResources(resowner, after_kinds, nkinds, nforget_al);
     270                 :             :     }
     271                 :             : 
     272                 :             :     /* Start releasing */
     273         [ +  - ]:           1 :     elog(NOTICE, "releasing resources before locks");
     274                 :           1 :     last_release_priority = 0;
     275                 :           1 :     ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
     276                 :             :     Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
     277                 :             : 
     278         [ +  - ]:           1 :     elog(NOTICE, "releasing locks");
     279                 :           1 :     last_release_priority = 0;
     280                 :           1 :     ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, false, false);
     281                 :             : 
     282         [ +  - ]:           1 :     elog(NOTICE, "releasing resources after locks");
     283                 :           1 :     last_release_priority = 0;
     284                 :           1 :     ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
     285                 :             :     Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
     286                 :             :     Assert(GetTotalResourceCount(after_kinds, nkinds) == 0);
     287                 :             : 
     288                 :           1 :     ResourceOwnerDelete(resowner);
     289                 :             : 
     290                 :           1 :     PG_RETURN_VOID();
     291                 :             : }
        

Generated by: LCOV version 2.0-1