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.84 2008/03/16 19:47:33 alvherre 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);
52 * SendSharedInvalidMessage
53 * Add a shared-cache-invalidation message to the global SI message queue.
56 SendSharedInvalidMessage(SharedInvalidationMessage *msg)
60 insertOK = SIInsertDataEntry(msg);
62 elog(DEBUG4, "SI buffer overflow");
66 * ReceiveSharedInvalidMessages
67 * Process shared-cache-invalidation messages waiting for this backend
69 * NOTE: it is entirely possible for this routine to be invoked recursively
70 * as a consequence of processing inside the invalFunction or resetFunction.
71 * Hence, we must be holding no SI resources when we call them. The only
72 * bad side-effect is that SIDelExpiredDataEntries might be called extra
73 * times on the way out of a nested call.
76 ReceiveSharedInvalidMessages(
77 void (*invalFunction) (SharedInvalidationMessage *msg),
78 void (*resetFunction) (void))
80 SharedInvalidationMessage data;
82 bool gotMessage = false;
87 * We can discard any pending catchup event, since we will not exit
88 * this loop until we're fully caught up.
90 catchupInterruptOccurred = 0;
92 getResult = SIGetDataEntry(MyBackendId, &data);
95 break; /* nothing more to do */
98 /* got a reset message */
99 elog(DEBUG4, "cache state reset");
104 /* got a normal data message */
105 invalFunction(&data);
110 /* If we got any messages, try to release dead messages */
112 SIDelExpiredDataEntries(false);
117 * CatchupInterruptHandler
119 * This is the signal handler for SIGUSR1.
121 * If we are idle (catchupInterruptEnabled is set), we can safely
122 * invoke ProcessCatchupEvent directly. Otherwise, just set a flag
123 * to do it later. (Note that it's quite possible for normal processing
124 * of the current transaction to cause ReceiveSharedInvalidMessages()
125 * to be run later on; in that case the flag will get cleared again,
126 * since there's no longer any reason to do anything.)
129 CatchupInterruptHandler(SIGNAL_ARGS)
131 int save_errno = errno;
134 * Note: this is a SIGNAL HANDLER. You must be very wary what you do
138 /* Don't joggle the elbow of proc_exit */
139 if (proc_exit_inprogress)
142 if (catchupInterruptEnabled)
144 bool save_ImmediateInterruptOK = ImmediateInterruptOK;
147 * We may be called while ImmediateInterruptOK is true; turn it off
148 * while messing with the catchup state. (We would have to save and
149 * restore it anyway, because PGSemaphore operations inside
150 * ProcessCatchupEvent() might reset it.)
152 ImmediateInterruptOK = false;
155 * I'm not sure whether some flavors of Unix might allow another
156 * SIGUSR1 occurrence to recursively interrupt this routine. To cope
157 * with the possibility, we do the same sort of dance that
158 * EnableCatchupInterrupt must do --- see that routine for comments.
160 catchupInterruptEnabled = 0; /* disable any recursive signal */
161 catchupInterruptOccurred = 1; /* do at least one iteration */
164 catchupInterruptEnabled = 1;
165 if (!catchupInterruptOccurred)
167 catchupInterruptEnabled = 0;
168 if (catchupInterruptOccurred)
170 /* Here, it is finally safe to do stuff. */
171 ProcessCatchupEvent();
176 * Restore ImmediateInterruptOK, and check for interrupts if needed.
178 ImmediateInterruptOK = save_ImmediateInterruptOK;
179 if (save_ImmediateInterruptOK)
180 CHECK_FOR_INTERRUPTS();
185 * In this path it is NOT SAFE to do much of anything, except this:
187 catchupInterruptOccurred = 1;
194 * EnableCatchupInterrupt
196 * This is called by the PostgresMain main loop just before waiting
197 * for a frontend command. We process any pending catchup events,
198 * and enable the signal handler to process future events directly.
200 * NOTE: the signal handler starts out disabled, and stays so until
201 * PostgresMain calls this the first time.
204 EnableCatchupInterrupt(void)
207 * This code is tricky because we are communicating with a signal handler
208 * that could interrupt us at any point. If we just checked
209 * catchupInterruptOccurred and then set catchupInterruptEnabled, we could
210 * fail to respond promptly to a signal that happens in between those two
211 * steps. (A very small time window, perhaps, but Murphy's Law says you
212 * can hit it...) Instead, we first set the enable flag, then test the
213 * occurred flag. If we see an unserviced interrupt has occurred, we
214 * re-clear the enable flag before going off to do the service work. (That
215 * prevents re-entrant invocation of ProcessCatchupEvent() if another
216 * interrupt occurs.) If an interrupt comes in between the setting and
217 * clearing of catchupInterruptEnabled, then it will have done the service
218 * work and left catchupInterruptOccurred zero, so we have to check again
219 * after clearing enable. The whole thing has to be in a loop in case
220 * another interrupt occurs while we're servicing the first. Once we get
221 * out of the loop, enable is set and we know there is no unserviced
224 * NB: an overenthusiastic optimizing compiler could easily break this
225 * code. Hopefully, they all understand what "volatile" means these days.
229 catchupInterruptEnabled = 1;
230 if (!catchupInterruptOccurred)
232 catchupInterruptEnabled = 0;
233 if (catchupInterruptOccurred)
234 ProcessCatchupEvent();
239 * DisableCatchupInterrupt
241 * This is called by the PostgresMain main loop just after receiving
242 * a frontend command. Signal handler execution of catchup events
243 * is disabled until the next EnableCatchupInterrupt call.
245 * The SIGUSR2 signal handler also needs to call this, so as to
246 * prevent conflicts if one signal interrupts the other. So we
247 * must return the previous state of the flag.
250 DisableCatchupInterrupt(void)
252 bool result = (catchupInterruptEnabled != 0);
254 catchupInterruptEnabled = 0;
260 * ProcessCatchupEvent
262 * Respond to a catchup event (SIGUSR1) from another backend.
264 * This is called either directly from the SIGUSR1 signal handler,
265 * or the next time control reaches the outer idle loop (assuming
266 * there's still anything to do by then).
269 ProcessCatchupEvent(void)
273 /* Must prevent SIGUSR2 interrupt while I am running */
274 notify_enabled = DisableNotifyInterrupt();
277 * What we need to do here is cause ReceiveSharedInvalidMessages() to run,
278 * which will do the necessary work and also reset the
279 * catchupInterruptOccurred flag. If we are inside a transaction we can
280 * just call AcceptInvalidationMessages() to do this. If we aren't, we
281 * start and immediately end a transaction; the call to
282 * AcceptInvalidationMessages() happens down inside transaction start.
284 * It is awfully tempting to just call AcceptInvalidationMessages()
285 * without the rest of the xact start/stop overhead, and I think that
286 * would actually work in the normal case; but I am not sure that things
287 * would clean up nicely if we got an error partway through.
289 if (IsTransactionOrTransactionBlock())
291 elog(DEBUG4, "ProcessCatchupEvent inside transaction");
292 AcceptInvalidationMessages();
296 elog(DEBUG4, "ProcessCatchupEvent outside transaction");
297 StartTransactionCommand();
298 CommitTransactionCommand();
302 EnableNotifyInterrupt();