Coverage Report

Created: 2025-06-15 06:31

/src/postgres/src/backend/postmaster/pmchild.c
Line
Count
Source (jump to first uncovered line)
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
MaxLivePostmasterChildren(void)
71
0
{
72
0
  if (num_pmchild_slots == 0)
73
0
    elog(ERROR, "PM child array not initialized yet");
74
0
  return num_pmchild_slots;
75
0
}
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
InitPostmasterChildSlots(void)
87
0
{
88
0
  int     slotno;
89
0
  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
0
  pmchild_pools[B_BACKEND].size = 2 * (MaxConnections + max_wal_senders);
101
102
0
  pmchild_pools[B_AUTOVAC_WORKER].size = autovacuum_worker_slots;
103
0
  pmchild_pools[B_BG_WORKER].size = max_worker_processes;
104
0
  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
0
  pmchild_pools[B_AUTOVAC_LAUNCHER].size = 1;
111
0
  pmchild_pools[B_SLOTSYNC_WORKER].size = 1;
112
0
  pmchild_pools[B_ARCHIVER].size = 1;
113
0
  pmchild_pools[B_BG_WRITER].size = 1;
114
0
  pmchild_pools[B_CHECKPOINTER].size = 1;
115
0
  pmchild_pools[B_STARTUP].size = 1;
116
0
  pmchild_pools[B_WAL_RECEIVER].size = 1;
117
0
  pmchild_pools[B_WAL_SUMMARIZER].size = 1;
118
0
  pmchild_pools[B_WAL_WRITER].size = 1;
119
0
  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
0
  num_pmchild_slots = 0;
125
0
  for (int i = 0; i < BACKEND_NUM_TYPES; i++)
126
0
    num_pmchild_slots += pmchild_pools[i].size;
127
128
  /* Initialize them */
129
0
  slots = palloc(num_pmchild_slots * sizeof(PMChild));
130
0
  slotno = 0;
131
0
  for (int btype = 0; btype < BACKEND_NUM_TYPES; btype++)
132
0
  {
133
0
    pmchild_pools[btype].first_slotno = slotno + 1;
134
0
    dlist_init(&pmchild_pools[btype].freelist);
135
136
0
    for (int j = 0; j < pmchild_pools[btype].size; j++)
137
0
    {
138
0
      slots[slotno].pid = 0;
139
0
      slots[slotno].child_slot = slotno + 1;
140
0
      slots[slotno].bkend_type = B_INVALID;
141
0
      slots[slotno].rw = NULL;
142
0
      slots[slotno].bgworker_notify = false;
143
0
      dlist_push_tail(&pmchild_pools[btype].freelist, &slots[slotno].elem);
144
0
      slotno++;
145
0
    }
146
0
  }
147
0
  Assert(slotno == num_pmchild_slots);
148
149
  /* Initialize other structures */
150
0
  dlist_init(&ActiveChildList);
151
0
}
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
AssignPostmasterChildSlot(BackendType btype)
163
0
{
164
0
  dlist_head *freelist;
165
0
  PMChild    *pmchild;
166
167
0
  if (pmchild_pools[btype].size == 0)
168
0
    elog(ERROR, "cannot allocate a PMChild slot for backend type %d", btype);
169
170
0
  freelist = &pmchild_pools[btype].freelist;
171
0
  if (dlist_is_empty(freelist))
172
0
    return NULL;
173
174
0
  pmchild = dlist_container(PMChild, elem, dlist_pop_head_node(freelist));
175
0
  pmchild->pid = 0;
176
0
  pmchild->bkend_type = btype;
177
0
  pmchild->rw = NULL;
178
0
  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
0
  if (!(pmchild->child_slot >= pmchild_pools[btype].first_slotno &&
185
0
      pmchild->child_slot < pmchild_pools[btype].first_slotno + pmchild_pools[btype].size))
186
0
  {
187
0
    elog(ERROR, "pmchild freelist for backend type %d is corrupt",
188
0
       pmchild->bkend_type);
189
0
  }
190
191
0
  dlist_push_head(&ActiveChildList, &pmchild->elem);
192
193
  /* Update the status in the shared memory array */
194
0
  MarkPostmasterChildSlotAssigned(pmchild->child_slot);
195
196
0
  elog(DEBUG2, "assigned pm child slot %d for %s",
197
0
     pmchild->child_slot, PostmasterChildName(btype));
198
199
0
  return pmchild;
200
0
}
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
AllocDeadEndChild(void)
209
0
{
210
0
  PMChild    *pmchild;
211
212
0
  elog(DEBUG2, "allocating dead-end child");
213
214
0
  pmchild = (PMChild *) palloc_extended(sizeof(PMChild), MCXT_ALLOC_NO_OOM);
215
0
  if (pmchild)
216
0
  {
217
0
    pmchild->pid = 0;
218
0
    pmchild->child_slot = 0;
219
0
    pmchild->bkend_type = B_DEAD_END_BACKEND;
220
0
    pmchild->rw = NULL;
221
0
    pmchild->bgworker_notify = false;
222
223
0
    dlist_push_head(&ActiveChildList, &pmchild->elem);
224
0
  }
225
226
0
  return pmchild;
227
0
}
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
ReleasePostmasterChildSlot(PMChild *pmchild)
237
0
{
238
0
  dlist_delete(&pmchild->elem);
239
0
  if (pmchild->bkend_type == B_DEAD_END_BACKEND)
240
0
  {
241
0
    elog(DEBUG2, "releasing dead-end backend");
242
0
    pfree(pmchild);
243
0
    return true;
244
0
  }
245
0
  else
246
0
  {
247
0
    PMChildPool *pool;
248
249
0
    elog(DEBUG2, "releasing pm child slot %d", pmchild->child_slot);
250
251
    /* WAL senders start out as regular backends, and share the pool */
252
0
    if (pmchild->bkend_type == B_WAL_SENDER)
253
0
      pool = &pmchild_pools[B_BACKEND];
254
0
    else
255
0
      pool = &pmchild_pools[pmchild->bkend_type];
256
257
    /* sanity check that we return the entry to the right pool */
258
0
    if (!(pmchild->child_slot >= pool->first_slotno &&
259
0
        pmchild->child_slot < pool->first_slotno + pool->size))
260
0
    {
261
0
      elog(ERROR, "pmchild freelist for backend type %d is corrupt",
262
0
         pmchild->bkend_type);
263
0
    }
264
265
0
    dlist_push_head(&pool->freelist, &pmchild->elem);
266
0
    return MarkPostmasterChildSlotUnassigned(pmchild->child_slot);
267
0
  }
268
0
}
269
270
/*
271
 * Find the PMChild entry of a running child process by PID.
272
 */
273
PMChild *
274
FindPostmasterChildByPid(int pid)
275
0
{
276
0
  dlist_iter  iter;
277
278
0
  dlist_foreach(iter, &ActiveChildList)
279
0
  {
280
0
    PMChild    *bp = dlist_container(PMChild, elem, iter.cur);
281
282
0
    if (bp->pid == pid)
283
0
      return bp;
284
0
  }
285
0
  return NULL;
286
0
}