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

Generated by: LCOV version 2.0-1