Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pmsignal.c
4 : * routines for signaling the postmaster from its child processes
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * src/backend/storage/ipc/pmsignal.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <signal.h>
18 : #include <unistd.h>
19 :
20 : #include "miscadmin.h"
21 : #include "postmaster/postmaster.h"
22 : #include "replication/walsender.h"
23 : #include "storage/pmsignal.h"
24 : #include "storage/shmem.h"
25 :
26 :
27 : /*
28 : * The postmaster is signaled by its children by sending SIGUSR1. The
29 : * specific reason is communicated via flags in shared memory. We keep
30 : * a boolean flag for each possible "reason", so that different reasons
31 : * can be signaled by different backends at the same time. (However,
32 : * if the same reason is signaled more than once simultaneously, the
33 : * postmaster will observe it only once.)
34 : *
35 : * The flags are actually declared as "volatile sig_atomic_t" for maximum
36 : * portability. This should ensure that loads and stores of the flag
37 : * values are atomic, allowing us to dispense with any explicit locking.
38 : *
39 : * In addition to the per-reason flags, we store a set of per-child-process
40 : * flags that are currently used only for detecting whether a backend has
41 : * exited without performing proper shutdown. The per-child-process flags
42 : * have three possible states: UNUSED, ASSIGNED, ACTIVE. An UNUSED slot is
43 : * available for assignment. An ASSIGNED slot is associated with a postmaster
44 : * child process, but either the process has not touched shared memory yet,
45 : * or it has successfully cleaned up after itself. A ACTIVE slot means the
46 : * process is actively using shared memory. The slots are assigned to
47 : * child processes at random, and postmaster.c is responsible for tracking
48 : * which one goes with which PID.
49 : *
50 : * Actually there is a fourth state, WALSENDER. This is just like ACTIVE,
51 : * but carries the extra information that the child is a WAL sender.
52 : * WAL senders too start in ACTIVE state, but switch to WALSENDER once they
53 : * start streaming the WAL (and they never go back to ACTIVE after that).
54 : */
55 :
56 : #define PM_CHILD_UNUSED 0 /* these values must fit in sig_atomic_t */
57 : #define PM_CHILD_ASSIGNED 1
58 : #define PM_CHILD_ACTIVE 2
59 : #define PM_CHILD_WALSENDER 3
60 :
61 : /* "typedef struct PMSignalData PMSignalData" appears in pmsignal.h */
62 : struct PMSignalData
63 : {
64 : /* per-reason flags */
65 : sig_atomic_t PMSignalFlags[NUM_PMSIGNALS];
66 : /* per-child-process flags */
67 : int num_child_flags; /* # of entries in PMChildFlags[] */
68 : int next_child_flag; /* next slot to try to assign */
69 : sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER];
70 : };
71 :
72 : NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL;
73 :
74 :
75 : /*
76 : * PMSignalShmemSize
77 : * Compute space needed for pmsignal.c's shared memory
78 : */
79 : Size
80 15 : PMSignalShmemSize(void)
81 : {
82 : Size size;
83 :
84 15 : size = offsetof(PMSignalData, PMChildFlags);
85 15 : size = add_size(size, mul_size(MaxLivePostmasterChildren(),
86 : sizeof(sig_atomic_t)));
87 :
88 15 : return size;
89 : }
90 :
91 : /*
92 : * PMSignalShmemInit - initialize during shared-memory creation
93 : */
94 : void
95 5 : PMSignalShmemInit(void)
96 : {
97 : bool found;
98 :
99 5 : PMSignalState = (PMSignalData *)
100 5 : ShmemInitStruct("PMSignalState", PMSignalShmemSize(), &found);
101 :
102 5 : if (!found)
103 : {
104 5 : MemSet(PMSignalState, 0, PMSignalShmemSize());
105 5 : PMSignalState->num_child_flags = MaxLivePostmasterChildren();
106 : }
107 5 : }
108 :
109 : /*
110 : * SendPostmasterSignal - signal the postmaster from a child process
111 : */
112 : void
113 120 : SendPostmasterSignal(PMSignalReason reason)
114 : {
115 : /* If called in a standalone backend, do nothing */
116 120 : if (!IsUnderPostmaster)
117 120 : return;
118 : /* Atomically set the proper flag */
119 120 : PMSignalState->PMSignalFlags[reason] = true;
120 : /* Send signal to postmaster */
121 120 : kill(PostmasterPid, SIGUSR1);
122 : }
123 :
124 : /*
125 : * CheckPostmasterSignal - check to see if a particular reason has been
126 : * signaled, and clear the signal flag. Should be called by postmaster
127 : * after receiving SIGUSR1.
128 : */
129 : bool
130 387 : CheckPostmasterSignal(PMSignalReason reason)
131 : {
132 : /* Careful here --- don't clear flag if we haven't seen it set */
133 387 : if (PMSignalState->PMSignalFlags[reason])
134 : {
135 37 : PMSignalState->PMSignalFlags[reason] = false;
136 37 : return true;
137 : }
138 350 : return false;
139 : }
140 :
141 :
142 : /*
143 : * AssignPostmasterChildSlot - select an unused slot for a new postmaster
144 : * child process, and set its state to ASSIGNED. Returns a slot number
145 : * (one to N).
146 : *
147 : * Only the postmaster is allowed to execute this routine, so we need no
148 : * special locking.
149 : */
150 : int
151 335 : AssignPostmasterChildSlot(void)
152 : {
153 335 : int slot = PMSignalState->next_child_flag;
154 : int n;
155 :
156 : /*
157 : * Scan for a free slot. We track the last slot assigned so as not to
158 : * waste time repeatedly rescanning low-numbered slots.
159 : */
160 336 : for (n = PMSignalState->num_child_flags; n > 0; n--)
161 : {
162 336 : if (--slot < 0)
163 2 : slot = PMSignalState->num_child_flags - 1;
164 336 : if (PMSignalState->PMChildFlags[slot] == PM_CHILD_UNUSED)
165 : {
166 335 : PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
167 335 : PMSignalState->next_child_flag = slot;
168 335 : return slot + 1;
169 : }
170 : }
171 :
172 : /* Out of slots ... should never happen, else postmaster.c messed up */
173 0 : elog(FATAL, "no free slots in PMChildFlags array");
174 : return 0; /* keep compiler quiet */
175 : }
176 :
177 : /*
178 : * ReleasePostmasterChildSlot - release a slot after death of a postmaster
179 : * child process. This must be called in the postmaster process.
180 : *
181 : * Returns true if the slot had been in ASSIGNED state (the expected case),
182 : * false otherwise (implying that the child failed to clean itself up).
183 : */
184 : bool
185 335 : ReleasePostmasterChildSlot(int slot)
186 : {
187 : bool result;
188 :
189 335 : Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
190 335 : slot--;
191 :
192 : /*
193 : * Note: the slot state might already be unused, because the logic in
194 : * postmaster.c is such that this might get called twice when a child
195 : * crashes. So we don't try to Assert anything about the state.
196 : */
197 335 : result = (PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
198 335 : PMSignalState->PMChildFlags[slot] = PM_CHILD_UNUSED;
199 335 : return result;
200 : }
201 :
202 : /*
203 : * IsPostmasterChildWalSender - check if given slot is in use by a
204 : * walsender process.
205 : */
206 : bool
207 0 : IsPostmasterChildWalSender(int slot)
208 : {
209 0 : Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
210 0 : slot--;
211 :
212 0 : if (PMSignalState->PMChildFlags[slot] == PM_CHILD_WALSENDER)
213 0 : return true;
214 : else
215 0 : return false;
216 : }
217 :
218 : /*
219 : * MarkPostmasterChildActive - mark a postmaster child as about to begin
220 : * actively using shared memory. This is called in the child process.
221 : */
222 : void
223 335 : MarkPostmasterChildActive(void)
224 : {
225 335 : int slot = MyPMChildSlot;
226 :
227 335 : Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
228 335 : slot--;
229 335 : Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
230 335 : PMSignalState->PMChildFlags[slot] = PM_CHILD_ACTIVE;
231 335 : }
232 :
233 : /*
234 : * MarkPostmasterChildWalSender - mark a postmaster child as a WAL sender
235 : * process. This is called in the child process, sometime after marking the
236 : * child as active.
237 : */
238 : void
239 0 : MarkPostmasterChildWalSender(void)
240 : {
241 0 : int slot = MyPMChildSlot;
242 :
243 0 : Assert(am_walsender);
244 :
245 0 : Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
246 0 : slot--;
247 0 : Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ACTIVE);
248 0 : PMSignalState->PMChildFlags[slot] = PM_CHILD_WALSENDER;
249 0 : }
250 :
251 : /*
252 : * MarkPostmasterChildInactive - mark a postmaster child as done using
253 : * shared memory. This is called in the child process.
254 : */
255 : void
256 335 : MarkPostmasterChildInactive(void)
257 : {
258 335 : int slot = MyPMChildSlot;
259 :
260 335 : Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
261 335 : slot--;
262 335 : Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ACTIVE ||
263 : PMSignalState->PMChildFlags[slot] == PM_CHILD_WALSENDER);
264 335 : PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
265 335 : }
266 :
267 :
268 : /*
269 : * PostmasterIsAlive - check whether postmaster process is still alive
270 : */
271 : bool
272 0 : PostmasterIsAlive(void)
273 : {
274 : #ifndef WIN32
275 : char c;
276 : ssize_t rc;
277 :
278 0 : rc = read(postmaster_alive_fds[POSTMASTER_FD_WATCH], &c, 1);
279 0 : if (rc < 0)
280 : {
281 0 : if (errno == EAGAIN || errno == EWOULDBLOCK)
282 0 : return true;
283 : else
284 0 : elog(FATAL, "read on postmaster death monitoring pipe failed: %m");
285 : }
286 0 : else if (rc > 0)
287 0 : elog(FATAL, "unexpected data in postmaster death monitoring pipe");
288 :
289 0 : return false;
290 : #else /* WIN32 */
291 : return (WaitForSingleObject(PostmasterHandle, 0) == WAIT_TIMEOUT);
292 : #endif /* WIN32 */
293 : }
|