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

Generated by: LCOV version 1.14