LCOV - code coverage report
Current view: top level - src/backend/postmaster - pmchild.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 82 87 94.3 %
Date: 2025-04-01 15:15:16 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pmchild.c
       4             :  *    Functions for keeping track of postmaster child processes.
       5             :  *
       6             :  * Postmaster keeps track of all child processes so that when a process exits,
       7             :  * it knows what kind of a process it was and can clean up accordingly.  Every
       8             :  * child process is allocated a PMChild struct from a fixed pool of structs.
       9             :  * The size of the pool is determined by various settings that configure how
      10             :  * many worker processes and backend connections are allowed, i.e.
      11             :  * autovacuum_worker_slots, max_worker_processes, max_wal_senders, and
      12             :  * max_connections.
      13             :  *
      14             :  * Dead-end backends are handled slightly differently.  There is no limit
      15             :  * on the number of dead-end backends, and they do not need unique IDs, so
      16             :  * their PMChild structs are allocated dynamically, not from a pool.
      17             :  *
      18             :  * The structures and functions in this file are private to the postmaster
      19             :  * process.  But note that there is an array in shared memory, managed by
      20             :  * pmsignal.c, that mirrors this.
      21             :  *
      22             :  *
      23             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
      24             :  * Portions Copyright (c) 1994, Regents of the University of California
      25             :  *
      26             :  * IDENTIFICATION
      27             :  *    src/backend/postmaster/pmchild.c
      28             :  *
      29             :  *-------------------------------------------------------------------------
      30             :  */
      31             : 
      32             : #include "postgres.h"
      33             : 
      34             : #include "miscadmin.h"
      35             : #include "postmaster/autovacuum.h"
      36             : #include "postmaster/postmaster.h"
      37             : #include "replication/walsender.h"
      38             : #include "storage/pmsignal.h"
      39             : #include "storage/proc.h"
      40             : 
      41             : /*
      42             :  * Freelists for different kinds of child processes.  We maintain separate
      43             :  * pools for each, so that for example launching a lot of regular backends
      44             :  * cannot prevent autovacuum or an aux process from launching.
      45             :  */
      46             : typedef struct PMChildPool
      47             : {
      48             :     int         size;           /* number of PMChild slots reserved for this
      49             :                                  * kind of processes */
      50             :     int         first_slotno;   /* first slot belonging to this pool */
      51             :     dlist_head  freelist;       /* currently unused PMChild entries */
      52             : } PMChildPool;
      53             : 
      54             : static PMChildPool pmchild_pools[BACKEND_NUM_TYPES];
      55             : NON_EXEC_STATIC int num_pmchild_slots = 0;
      56             : 
      57             : /*
      58             :  * List of active child processes.  This includes dead-end children.
      59             :  */
      60             : dlist_head  ActiveChildList;
      61             : 
      62             : /*
      63             :  * MaxLivePostmasterChildren
      64             :  *
      65             :  * This reports the number of postmaster child processes that can be active.
      66             :  * It includes all children except for dead-end children.  This allows the
      67             :  * array in shared memory (PMChildFlags) to have a fixed maximum size.
      68             :  */
      69             : int
      70        9890 : MaxLivePostmasterChildren(void)
      71             : {
      72        9890 :     if (num_pmchild_slots == 0)
      73           0 :         elog(ERROR, "PM child array not initialized yet");
      74        9890 :     return num_pmchild_slots;
      75             : }
      76             : 
      77             : /*
      78             :  * Initialize at postmaster startup
      79             :  *
      80             :  * Note: This is not called on crash restart.  We rely on PMChild entries to
      81             :  * remain valid through the restart process.  This is important because the
      82             :  * syslogger survives through the crash restart process, so we must not
      83             :  * invalidate its PMChild slot.
      84             :  */
      85             : void
      86        2028 : InitPostmasterChildSlots(void)
      87             : {
      88             :     int         slotno;
      89             :     PMChild    *slots;
      90             : 
      91             :     /*
      92             :      * We allow more connections here than we can have backends because some
      93             :      * might still be authenticating; they might fail auth, or some existing
      94             :      * backend might exit before the auth cycle is completed.  The exact
      95             :      * MaxConnections limit is enforced when a new backend tries to join the
      96             :      * PGPROC array.
      97             :      *
      98             :      * WAL senders start out as regular backends, so they share the same pool.
      99             :      */
     100        2028 :     pmchild_pools[B_BACKEND].size = 2 * (MaxConnections + max_wal_senders);
     101             : 
     102        2028 :     pmchild_pools[B_AUTOVAC_WORKER].size = autovacuum_worker_slots;
     103        2028 :     pmchild_pools[B_BG_WORKER].size = max_worker_processes;
     104        2028 :     pmchild_pools[B_IO_WORKER].size = MAX_IO_WORKERS;
     105             : 
     106             :     /*
     107             :      * There can be only one of each of these running at a time.  They each
     108             :      * get their own pool of just one entry.
     109             :      */
     110        2028 :     pmchild_pools[B_AUTOVAC_LAUNCHER].size = 1;
     111        2028 :     pmchild_pools[B_SLOTSYNC_WORKER].size = 1;
     112        2028 :     pmchild_pools[B_ARCHIVER].size = 1;
     113        2028 :     pmchild_pools[B_BG_WRITER].size = 1;
     114        2028 :     pmchild_pools[B_CHECKPOINTER].size = 1;
     115        2028 :     pmchild_pools[B_STARTUP].size = 1;
     116        2028 :     pmchild_pools[B_WAL_RECEIVER].size = 1;
     117        2028 :     pmchild_pools[B_WAL_SUMMARIZER].size = 1;
     118        2028 :     pmchild_pools[B_WAL_WRITER].size = 1;
     119        2028 :     pmchild_pools[B_LOGGER].size = 1;
     120             : 
     121             :     /* The rest of the pmchild_pools are left at zero size */
     122             : 
     123             :     /* Count the total number of slots */
     124        2028 :     num_pmchild_slots = 0;
     125       38532 :     for (int i = 0; i < BACKEND_NUM_TYPES; i++)
     126       36504 :         num_pmchild_slots += pmchild_pools[i].size;
     127             : 
     128             :     /* Initialize them */
     129        2028 :     slots = palloc(num_pmchild_slots * sizeof(PMChild));
     130        2028 :     slotno = 0;
     131       38532 :     for (int btype = 0; btype < BACKEND_NUM_TYPES; btype++)
     132             :     {
     133       36504 :         pmchild_pools[btype].first_slotno = slotno + 1;
     134       36504 :         dlist_init(&pmchild_pools[btype].freelist);
     135             : 
     136      453412 :         for (int j = 0; j < pmchild_pools[btype].size; j++)
     137             :         {
     138      416908 :             slots[slotno].pid = 0;
     139      416908 :             slots[slotno].child_slot = slotno + 1;
     140      416908 :             slots[slotno].bkend_type = B_INVALID;
     141      416908 :             slots[slotno].rw = NULL;
     142      416908 :             slots[slotno].bgworker_notify = false;
     143      416908 :             dlist_push_tail(&pmchild_pools[btype].freelist, &slots[slotno].elem);
     144      416908 :             slotno++;
     145             :         }
     146             :     }
     147             :     Assert(slotno == num_pmchild_slots);
     148             : 
     149             :     /* Initialize other structures */
     150        2028 :     dlist_init(&ActiveChildList);
     151        2028 : }
     152             : 
     153             : /*
     154             :  * Allocate a PMChild entry for a postmaster child process of given type.
     155             :  *
     156             :  * The entry is taken from the right pool for the type.
     157             :  *
     158             :  * pmchild->child_slot in the returned struct is unique among all active child
     159             :  * processes.
     160             :  */
     161             : PMChild *
     162       47852 : AssignPostmasterChildSlot(BackendType btype)
     163             : {
     164             :     dlist_head *freelist;
     165             :     PMChild    *pmchild;
     166             : 
     167       47852 :     if (pmchild_pools[btype].size == 0)
     168           0 :         elog(ERROR, "cannot allocate a PMChild slot for backend type %d", btype);
     169             : 
     170       47852 :     freelist = &pmchild_pools[btype].freelist;
     171       47852 :     if (dlist_is_empty(freelist))
     172          56 :         return NULL;
     173             : 
     174       47796 :     pmchild = dlist_container(PMChild, elem, dlist_pop_head_node(freelist));
     175       47796 :     pmchild->pid = 0;
     176       47796 :     pmchild->bkend_type = btype;
     177       47796 :     pmchild->rw = NULL;
     178       47796 :     pmchild->bgworker_notify = true;
     179             : 
     180             :     /*
     181             :      * pmchild->child_slot for each entry was initialized when the array of
     182             :      * slots was allocated.  Sanity check it.
     183             :      */
     184       47796 :     if (!(pmchild->child_slot >= pmchild_pools[btype].first_slotno &&
     185       47796 :           pmchild->child_slot < pmchild_pools[btype].first_slotno + pmchild_pools[btype].size))
     186             :     {
     187           0 :         elog(ERROR, "pmchild freelist for backend type %d is corrupt",
     188             :              pmchild->bkend_type);
     189             :     }
     190             : 
     191       47796 :     dlist_push_head(&ActiveChildList, &pmchild->elem);
     192             : 
     193             :     /* Update the status in the shared memory array */
     194       47796 :     MarkPostmasterChildSlotAssigned(pmchild->child_slot);
     195             : 
     196       47796 :     elog(DEBUG2, "assigned pm child slot %d for %s",
     197             :          pmchild->child_slot, PostmasterChildName(btype));
     198             : 
     199       47796 :     return pmchild;
     200             : }
     201             : 
     202             : /*
     203             :  * Allocate a PMChild struct for a dead-end backend.  Dead-end children are
     204             :  * not assigned a child_slot number.  The struct is palloc'd; returns NULL if
     205             :  * out of memory.
     206             :  */
     207             : PMChild *
     208         316 : AllocDeadEndChild(void)
     209             : {
     210             :     PMChild    *pmchild;
     211             : 
     212         316 :     elog(DEBUG2, "allocating dead-end child");
     213             : 
     214         316 :     pmchild = (PMChild *) palloc_extended(sizeof(PMChild), MCXT_ALLOC_NO_OOM);
     215         316 :     if (pmchild)
     216             :     {
     217         316 :         pmchild->pid = 0;
     218         316 :         pmchild->child_slot = 0;
     219         316 :         pmchild->bkend_type = B_DEAD_END_BACKEND;
     220         316 :         pmchild->rw = NULL;
     221         316 :         pmchild->bgworker_notify = false;
     222             : 
     223         316 :         dlist_push_head(&ActiveChildList, &pmchild->elem);
     224             :     }
     225             : 
     226         316 :     return pmchild;
     227             : }
     228             : 
     229             : /*
     230             :  * Release a PMChild slot, after the child process has exited.
     231             :  *
     232             :  * Returns true if the child detached cleanly from shared memory, false
     233             :  * otherwise (see MarkPostmasterChildSlotUnassigned).
     234             :  */
     235             : bool
     236       48048 : ReleasePostmasterChildSlot(PMChild *pmchild)
     237             : {
     238       48048 :     dlist_delete(&pmchild->elem);
     239       48048 :     if (pmchild->bkend_type == B_DEAD_END_BACKEND)
     240             :     {
     241         316 :         elog(DEBUG2, "releasing dead-end backend");
     242         316 :         pfree(pmchild);
     243         316 :         return true;
     244             :     }
     245             :     else
     246             :     {
     247             :         PMChildPool *pool;
     248             : 
     249       47732 :         elog(DEBUG2, "releasing pm child slot %d", pmchild->child_slot);
     250             : 
     251             :         /* WAL senders start out as regular backends, and share the pool */
     252       47732 :         if (pmchild->bkend_type == B_WAL_SENDER)
     253          68 :             pool = &pmchild_pools[B_BACKEND];
     254             :         else
     255       47664 :             pool = &pmchild_pools[pmchild->bkend_type];
     256             : 
     257             :         /* sanity check that we return the entry to the right pool */
     258       47732 :         if (!(pmchild->child_slot >= pool->first_slotno &&
     259       47732 :               pmchild->child_slot < pool->first_slotno + pool->size))
     260             :         {
     261           0 :             elog(ERROR, "pmchild freelist for backend type %d is corrupt",
     262             :                  pmchild->bkend_type);
     263             :         }
     264             : 
     265       47732 :         dlist_push_head(&pool->freelist, &pmchild->elem);
     266       47732 :         return MarkPostmasterChildSlotUnassigned(pmchild->child_slot);
     267             :     }
     268             : }
     269             : 
     270             : /*
     271             :  * Find the PMChild entry of a running child process by PID.
     272             :  */
     273             : PMChild *
     274       34886 : FindPostmasterChildByPid(int pid)
     275             : {
     276             :     dlist_iter  iter;
     277             : 
     278       65314 :     dlist_foreach(iter, &ActiveChildList)
     279             :     {
     280       65314 :         PMChild    *bp = dlist_container(PMChild, elem, iter.cur);
     281             : 
     282       65314 :         if (bp->pid == pid)
     283       34886 :             return bp;
     284             :     }
     285           0 :     return NULL;
     286             : }

Generated by: LCOV version 1.14