1 /*-------------------------------------------------------------------------
4 * POSTGRES shared cache invalidation communication code.
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.83 2008/01/01 19:45:51 momjian Exp $
13 *-------------------------------------------------------------------------
19 #include "access/xact.h"
20 #include "commands/async.h"
21 #include "miscadmin.h"
22 #include "storage/backendid.h"
23 #include "storage/ipc.h"
24 #include "storage/proc.h"
25 #include "storage/sinvaladt.h"
26 #include "utils/inval.h"
30 * Because backends sitting idle will not be reading sinval events, we
31 * need a way to give an idle backend a swift kick in the rear and make
32 * it catch up before the sinval queue overflows and forces everyone
33 * through a cache reset exercise. This is done by broadcasting SIGUSR1
34 * to all backends when the queue is threatening to become full.
36 * State for catchup events consists of two flags: one saying whether
37 * the signal handler is currently allowed to call ProcessCatchupEvent
38 * directly, and one saying whether the signal has occurred but the handler
39 * was not allowed to call ProcessCatchupEvent at the time.
41 * NB: the "volatile" on these declarations is critical! If your compiler
42 * does not grok "volatile", you'd be best advised to compile this file
43 * with all optimization turned off.
45 static volatile int catchupInterruptEnabled = 0;
46 static volatile int catchupInterruptOccurred = 0;
48 static void ProcessCatchupEvent(void);
51 /****************************************************************************/
52 /* CreateSharedInvalidationState() Initialize SI buffer */
54 /* should be called only by the POSTMASTER */
55 /****************************************************************************/
57 CreateSharedInvalidationState(void)
59 /* SInvalLock must be initialized already, during LWLock init */
64 * InitBackendSharedInvalidationState
65 * Initialize new backend's state info in buffer segment.
68 InitBackendSharedInvalidationState(void)
72 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
73 flag = SIBackendInit(shmInvalBuffer);
74 LWLockRelease(SInvalLock);
75 if (flag < 0) /* unexpected problem */
76 elog(FATAL, "shared cache invalidation initialization failed");
77 if (flag == 0) /* expected problem: MaxBackends exceeded */
79 (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
80 errmsg("sorry, too many clients already")));
84 * SendSharedInvalidMessage
85 * Add a shared-cache-invalidation message to the global SI message queue.
88 SendSharedInvalidMessage(SharedInvalidationMessage *msg)
92 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
93 insertOK = SIInsertDataEntry(shmInvalBuffer, msg);
94 LWLockRelease(SInvalLock);
96 elog(DEBUG4, "SI buffer overflow");
100 * ReceiveSharedInvalidMessages
101 * Process shared-cache-invalidation messages waiting for this backend
103 * NOTE: it is entirely possible for this routine to be invoked recursively
104 * as a consequence of processing inside the invalFunction or resetFunction.
105 * Hence, we must be holding no SI resources when we call them. The only
106 * bad side-effect is that SIDelExpiredDataEntries might be called extra
107 * times on the way out of a nested call.
110 ReceiveSharedInvalidMessages(
111 void (*invalFunction) (SharedInvalidationMessage *msg),
112 void (*resetFunction) (void))
114 SharedInvalidationMessage data;
116 bool gotMessage = false;
121 * We can discard any pending catchup event, since we will not exit
122 * this loop until we're fully caught up.
124 catchupInterruptOccurred = 0;
127 * We can run SIGetDataEntry in parallel with other backends running
128 * SIGetDataEntry for themselves, since each instance will modify only
129 * fields of its own backend's ProcState, and no instance will look at
130 * fields of other backends' ProcStates. We express this by grabbing
131 * SInvalLock in shared mode. Note that this is not exactly the
132 * normal (read-only) interpretation of a shared lock! Look closely at
133 * the interactions before allowing SInvalLock to be grabbed in shared
134 * mode for any other reason!
136 LWLockAcquire(SInvalLock, LW_SHARED);
137 getResult = SIGetDataEntry(shmInvalBuffer, MyBackendId, &data);
138 LWLockRelease(SInvalLock);
141 break; /* nothing more to do */
144 /* got a reset message */
145 elog(DEBUG4, "cache state reset");
150 /* got a normal data message */
151 invalFunction(&data);
156 /* If we got any messages, try to release dead messages */
159 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
160 SIDelExpiredDataEntries(shmInvalBuffer);
161 LWLockRelease(SInvalLock);
167 * CatchupInterruptHandler
169 * This is the signal handler for SIGUSR1.
171 * If we are idle (catchupInterruptEnabled is set), we can safely
172 * invoke ProcessCatchupEvent directly. Otherwise, just set a flag
173 * to do it later. (Note that it's quite possible for normal processing
174 * of the current transaction to cause ReceiveSharedInvalidMessages()
175 * to be run later on; in that case the flag will get cleared again,
176 * since there's no longer any reason to do anything.)
179 CatchupInterruptHandler(SIGNAL_ARGS)
181 int save_errno = errno;
184 * Note: this is a SIGNAL HANDLER. You must be very wary what you do
188 /* Don't joggle the elbow of proc_exit */
189 if (proc_exit_inprogress)
192 if (catchupInterruptEnabled)
194 bool save_ImmediateInterruptOK = ImmediateInterruptOK;
197 * We may be called while ImmediateInterruptOK is true; turn it off
198 * while messing with the catchup state. (We would have to save and
199 * restore it anyway, because PGSemaphore operations inside
200 * ProcessCatchupEvent() might reset it.)
202 ImmediateInterruptOK = false;
205 * I'm not sure whether some flavors of Unix might allow another
206 * SIGUSR1 occurrence to recursively interrupt this routine. To cope
207 * with the possibility, we do the same sort of dance that
208 * EnableCatchupInterrupt must do --- see that routine for comments.
210 catchupInterruptEnabled = 0; /* disable any recursive signal */
211 catchupInterruptOccurred = 1; /* do at least one iteration */
214 catchupInterruptEnabled = 1;
215 if (!catchupInterruptOccurred)
217 catchupInterruptEnabled = 0;
218 if (catchupInterruptOccurred)
220 /* Here, it is finally safe to do stuff. */
221 ProcessCatchupEvent();
226 * Restore ImmediateInterruptOK, and check for interrupts if needed.
228 ImmediateInterruptOK = save_ImmediateInterruptOK;
229 if (save_ImmediateInterruptOK)
230 CHECK_FOR_INTERRUPTS();
235 * In this path it is NOT SAFE to do much of anything, except this:
237 catchupInterruptOccurred = 1;
244 * EnableCatchupInterrupt
246 * This is called by the PostgresMain main loop just before waiting
247 * for a frontend command. We process any pending catchup events,
248 * and enable the signal handler to process future events directly.
250 * NOTE: the signal handler starts out disabled, and stays so until
251 * PostgresMain calls this the first time.
254 EnableCatchupInterrupt(void)
257 * This code is tricky because we are communicating with a signal handler
258 * that could interrupt us at any point. If we just checked
259 * catchupInterruptOccurred and then set catchupInterruptEnabled, we could
260 * fail to respond promptly to a signal that happens in between those two
261 * steps. (A very small time window, perhaps, but Murphy's Law says you
262 * can hit it...) Instead, we first set the enable flag, then test the
263 * occurred flag. If we see an unserviced interrupt has occurred, we
264 * re-clear the enable flag before going off to do the service work. (That
265 * prevents re-entrant invocation of ProcessCatchupEvent() if another
266 * interrupt occurs.) If an interrupt comes in between the setting and
267 * clearing of catchupInterruptEnabled, then it will have done the service
268 * work and left catchupInterruptOccurred zero, so we have to check again
269 * after clearing enable. The whole thing has to be in a loop in case
270 * another interrupt occurs while we're servicing the first. Once we get
271 * out of the loop, enable is set and we know there is no unserviced
274 * NB: an overenthusiastic optimizing compiler could easily break this
275 * code. Hopefully, they all understand what "volatile" means these days.
279 catchupInterruptEnabled = 1;
280 if (!catchupInterruptOccurred)
282 catchupInterruptEnabled = 0;
283 if (catchupInterruptOccurred)
284 ProcessCatchupEvent();
289 * DisableCatchupInterrupt
291 * This is called by the PostgresMain main loop just after receiving
292 * a frontend command. Signal handler execution of catchup events
293 * is disabled until the next EnableCatchupInterrupt call.
295 * The SIGUSR2 signal handler also needs to call this, so as to
296 * prevent conflicts if one signal interrupts the other. So we
297 * must return the previous state of the flag.
300 DisableCatchupInterrupt(void)
302 bool result = (catchupInterruptEnabled != 0);
304 catchupInterruptEnabled = 0;
310 * ProcessCatchupEvent
312 * Respond to a catchup event (SIGUSR1) from another backend.
314 * This is called either directly from the SIGUSR1 signal handler,
315 * or the next time control reaches the outer idle loop (assuming
316 * there's still anything to do by then).
319 ProcessCatchupEvent(void)
323 /* Must prevent SIGUSR2 interrupt while I am running */
324 notify_enabled = DisableNotifyInterrupt();
327 * What we need to do here is cause ReceiveSharedInvalidMessages() to run,
328 * which will do the necessary work and also reset the
329 * catchupInterruptOccurred flag. If we are inside a transaction we can
330 * just call AcceptInvalidationMessages() to do this. If we aren't, we
331 * start and immediately end a transaction; the call to
332 * AcceptInvalidationMessages() happens down inside transaction start.
334 * It is awfully tempting to just call AcceptInvalidationMessages()
335 * without the rest of the xact start/stop overhead, and I think that
336 * would actually work in the normal case; but I am not sure that things
337 * would clean up nicely if we got an error partway through.
339 if (IsTransactionOrTransactionBlock())
341 elog(DEBUG4, "ProcessCatchupEvent inside transaction");
342 AcceptInvalidationMessages();
346 elog(DEBUG4, "ProcessCatchupEvent outside transaction");
347 StartTransactionCommand();
348 CommitTransactionCommand();
352 EnableNotifyInterrupt();