LCOV - code coverage report
Current view: top level - src/test/modules/test_resowner - test_resowner_many.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.4 % 114 103
Test Date: 2026-03-19 03:16:07 Functions: 87.5 % 8 7
Legend: Lines:     hit not hit

            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