LCOV - code coverage report
Current view: top level - src/test/modules/test_resowner - test_resowner_many.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 136 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 8 0
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            0 : ReleaseManyTestResource(Datum res)
      68              : {
      69            0 :         ManyTestResource *mres = (ManyTestResource *) DatumGetPointer(res);
      70              : 
      71            0 :         elog(DEBUG1, "releasing resource %p from %s", mres, mres->kind->desc.name);
      72            0 :         Assert(last_release_priority <= mres->kind->desc.release_priority);
      73              : 
      74            0 :         dlist_delete(&mres->node);
      75            0 :         mres->kind->nreleased++;
      76            0 :         last_release_priority = mres->kind->desc.release_priority;
      77            0 :         pfree(mres);
      78            0 : }
      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            0 : }
      94              : 
      95              : static void
      96            0 : InitManyTestResourceKind(ManyTestResourceKind *kind, char *name,
      97              :                                                  ResourceReleasePhase phase, uint32 priority)
      98              : {
      99            0 :         kind->desc.name = name;
     100            0 :         kind->desc.release_phase = phase;
     101            0 :         kind->desc.release_priority = priority;
     102            0 :         kind->desc.ReleaseResource = ReleaseManyTestResource;
     103            0 :         kind->desc.DebugPrint = PrintManyTest;
     104            0 :         kind->nremembered = 0;
     105            0 :         kind->nforgotten = 0;
     106            0 :         kind->nreleased = 0;
     107            0 :         kind->nleaked = 0;
     108            0 :         dlist_init(&kind->current_resources);
     109            0 : }
     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            0 : RememberManyTestResources(ResourceOwner owner,
     117              :                                                   ManyTestResourceKind *kinds, int nkinds,
     118              :                                                   int nresources)
     119              : {
     120            0 :         int                     kind_idx = 0;
     121              : 
     122            0 :         for (int i = 0; i < nresources; i++)
     123              :         {
     124            0 :                 ManyTestResource *mres = palloc_object(ManyTestResource);
     125              : 
     126            0 :                 mres->kind = &kinds[kind_idx];
     127            0 :                 dlist_node_init(&mres->node);
     128              : 
     129            0 :                 ResourceOwnerEnlarge(owner);
     130            0 :                 ResourceOwnerRemember(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
     131            0 :                 kinds[kind_idx].nremembered++;
     132            0 :                 dlist_push_tail(&kinds[kind_idx].current_resources, &mres->node);
     133              : 
     134            0 :                 elog(DEBUG1, "remembered resource %p from %s", mres, mres->kind->desc.name);
     135              : 
     136            0 :                 kind_idx = (kind_idx + 1) % nkinds;
     137            0 :         }
     138            0 : }
     139              : 
     140              : /*
     141              :  * Forget 'nresources' resources, in round robin fashion from 'kinds'.
     142              :  */
     143              : static void
     144            0 : ForgetManyTestResources(ResourceOwner owner,
     145              :                                                 ManyTestResourceKind *kinds, int nkinds,
     146              :                                                 int nresources)
     147              : {
     148            0 :         int                     kind_idx = 0;
     149            0 :         int                     ntotal;
     150              : 
     151            0 :         ntotal = GetTotalResourceCount(kinds, nkinds);
     152            0 :         if (ntotal < nresources)
     153            0 :                 elog(PANIC, "cannot free %d resources, only %d remembered", nresources, ntotal);
     154              : 
     155            0 :         for (int i = 0; i < nresources; i++)
     156              :         {
     157            0 :                 bool            found = false;
     158              : 
     159            0 :                 for (int j = 0; j < nkinds; j++)
     160              :                 {
     161            0 :                         kind_idx = (kind_idx + 1) % nkinds;
     162            0 :                         if (!dlist_is_empty(&kinds[kind_idx].current_resources))
     163              :                         {
     164            0 :                                 ManyTestResource *mres = dlist_head_element(ManyTestResource, node, &kinds[kind_idx].current_resources);
     165              : 
     166            0 :                                 ResourceOwnerForget(owner, PointerGetDatum(mres), &kinds[kind_idx].desc);
     167            0 :                                 kinds[kind_idx].nforgotten++;
     168            0 :                                 dlist_delete(&mres->node);
     169            0 :                                 pfree(mres);
     170              : 
     171            0 :                                 found = true;
     172              :                                 break;
     173            0 :                         }
     174            0 :                 }
     175            0 :                 if (!found)
     176            0 :                         elog(ERROR, "could not find a test resource to forget");
     177            0 :         }
     178            0 : }
     179              : 
     180              : /*
     181              :  * Get total number of currently active resources among 'kinds'.
     182              :  */
     183              : static int
     184            0 : GetTotalResourceCount(ManyTestResourceKind *kinds, int nkinds)
     185              : {
     186            0 :         int                     ntotal = 0;
     187              : 
     188            0 :         for (int i = 0; i < nkinds; i++)
     189            0 :                 ntotal += kinds[i].nremembered - kinds[i].nforgotten - kinds[i].nreleased;
     190              : 
     191            0 :         return ntotal;
     192            0 : }
     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            0 : PG_FUNCTION_INFO_V1(test_resowner_many);
     202              : Datum
     203            0 : test_resowner_many(PG_FUNCTION_ARGS)
     204              : {
     205            0 :         int32           nkinds = PG_GETARG_INT32(0);
     206            0 :         int32           nremember_bl = PG_GETARG_INT32(1);
     207            0 :         int32           nforget_bl = PG_GETARG_INT32(2);
     208            0 :         int32           nremember_al = PG_GETARG_INT32(3);
     209            0 :         int32           nforget_al = PG_GETARG_INT32(4);
     210              : 
     211            0 :         ResourceOwner resowner;
     212              : 
     213            0 :         ManyTestResourceKind *before_kinds;
     214            0 :         ManyTestResourceKind *after_kinds;
     215              : 
     216              :         /* Sanity check the arguments */
     217            0 :         if (nkinds < 0)
     218            0 :                 elog(ERROR, "nkinds must be >= 0");
     219            0 :         if (nremember_bl < 0)
     220            0 :                 elog(ERROR, "nremember_bl must be >= 0");
     221            0 :         if (nforget_bl < 0 || nforget_bl > nremember_bl)
     222            0 :                 elog(ERROR, "nforget_bl must between 0 and 'nremember_bl'");
     223            0 :         if (nremember_al < 0)
     224            0 :                 elog(ERROR, "nremember_al must be greater than zero");
     225            0 :         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            0 :         before_kinds = palloc(nkinds * sizeof(ManyTestResourceKind));
     230            0 :         for (int i = 0; i < nkinds; i++)
     231              :         {
     232            0 :                 InitManyTestResourceKind(&before_kinds[i],
     233            0 :                                                                  psprintf("resource before locks %d", i),
     234              :                                                                  RESOURCE_RELEASE_BEFORE_LOCKS,
     235            0 :                                                                  RELEASE_PRIO_FIRST + i);
     236            0 :         }
     237            0 :         after_kinds = palloc(nkinds * sizeof(ManyTestResourceKind));
     238            0 :         for (int i = 0; i < nkinds; i++)
     239              :         {
     240            0 :                 InitManyTestResourceKind(&after_kinds[i],
     241            0 :                                                                  psprintf("resource after locks %d", i),
     242              :                                                                  RESOURCE_RELEASE_AFTER_LOCKS,
     243            0 :                                                                  RELEASE_PRIO_FIRST + i);
     244            0 :         }
     245              : 
     246            0 :         resowner = ResourceOwnerCreate(CurrentResourceOwner, "TestOwner");
     247              : 
     248              :         /* Remember a bunch of resources */
     249            0 :         if (nremember_bl > 0)
     250              :         {
     251            0 :                 elog(NOTICE, "remembering %d before-locks resources", nremember_bl);
     252            0 :                 RememberManyTestResources(resowner, before_kinds, nkinds, nremember_bl);
     253            0 :         }
     254            0 :         if (nremember_al > 0)
     255              :         {
     256            0 :                 elog(NOTICE, "remembering %d after-locks resources", nremember_al);
     257            0 :                 RememberManyTestResources(resowner, after_kinds, nkinds, nremember_al);
     258            0 :         }
     259              : 
     260              :         /* Forget what was remembered */
     261            0 :         if (nforget_bl > 0)
     262              :         {
     263            0 :                 elog(NOTICE, "forgetting %d before-locks resources", nforget_bl);
     264            0 :                 ForgetManyTestResources(resowner, before_kinds, nkinds, nforget_bl);
     265            0 :         }
     266              : 
     267            0 :         if (nforget_al > 0)
     268              :         {
     269            0 :                 elog(NOTICE, "forgetting %d after-locks resources", nforget_al);
     270            0 :                 ForgetManyTestResources(resowner, after_kinds, nkinds, nforget_al);
     271            0 :         }
     272              : 
     273              :         /* Start releasing */
     274            0 :         elog(NOTICE, "releasing resources before locks");
     275            0 :         current_release_phase = RESOURCE_RELEASE_BEFORE_LOCKS;
     276            0 :         last_release_priority = 0;
     277            0 :         ResourceOwnerRelease(resowner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false);
     278            0 :         Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
     279              : 
     280            0 :         elog(NOTICE, "releasing locks");
     281            0 :         current_release_phase = RESOURCE_RELEASE_LOCKS;
     282            0 :         last_release_priority = 0;
     283            0 :         ResourceOwnerRelease(resowner, RESOURCE_RELEASE_LOCKS, false, false);
     284              : 
     285            0 :         elog(NOTICE, "releasing resources after locks");
     286            0 :         current_release_phase = RESOURCE_RELEASE_AFTER_LOCKS;
     287            0 :         last_release_priority = 0;
     288            0 :         ResourceOwnerRelease(resowner, RESOURCE_RELEASE_AFTER_LOCKS, false, false);
     289            0 :         Assert(GetTotalResourceCount(before_kinds, nkinds) == 0);
     290            0 :         Assert(GetTotalResourceCount(after_kinds, nkinds) == 0);
     291              : 
     292            0 :         ResourceOwnerDelete(resowner);
     293              : 
     294            0 :         PG_RETURN_VOID();
     295            0 : }
        

Generated by: LCOV version 2.3.2-1