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

Generated by: LCOV version 1.14