]> granicus.if.org Git - postgresql/blob - src/backend/storage/ipc/sinval.c
d30d04aa31c76a84ea90798fdc1fed6b4ee19131
[postgresql] / src / backend / storage / ipc / sinval.c
1 /*-------------------------------------------------------------------------
2  *
3  * sinval.c
4  *        POSTGRES shared cache invalidation communication code.
5  *
6  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.83 2008/01/01 19:45:51 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <signal.h>
18
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"
27
28
29 /*
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.
35  *
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.
40  *
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.
44  */
45 static volatile int catchupInterruptEnabled = 0;
46 static volatile int catchupInterruptOccurred = 0;
47
48 static void ProcessCatchupEvent(void);
49
50
51 /****************************************************************************/
52 /*      CreateSharedInvalidationState()          Initialize SI buffer                           */
53 /*                                                                                                                                                      */
54 /*      should be called only by the POSTMASTER                                                                 */
55 /****************************************************************************/
56 void
57 CreateSharedInvalidationState(void)
58 {
59         /* SInvalLock must be initialized already, during LWLock init */
60         SIBufferInit();
61 }
62
63 /*
64  * InitBackendSharedInvalidationState
65  *              Initialize new backend's state info in buffer segment.
66  */
67 void
68 InitBackendSharedInvalidationState(void)
69 {
70         int                     flag;
71
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 */
78                 ereport(FATAL,
79                                 (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
80                                  errmsg("sorry, too many clients already")));
81 }
82
83 /*
84  * SendSharedInvalidMessage
85  *      Add a shared-cache-invalidation message to the global SI message queue.
86  */
87 void
88 SendSharedInvalidMessage(SharedInvalidationMessage *msg)
89 {
90         bool            insertOK;
91
92         LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
93         insertOK = SIInsertDataEntry(shmInvalBuffer, msg);
94         LWLockRelease(SInvalLock);
95         if (!insertOK)
96                 elog(DEBUG4, "SI buffer overflow");
97 }
98
99 /*
100  * ReceiveSharedInvalidMessages
101  *              Process shared-cache-invalidation messages waiting for this backend
102  *
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.
108  */
109 void
110 ReceiveSharedInvalidMessages(
111                                           void (*invalFunction) (SharedInvalidationMessage *msg),
112                                                          void (*resetFunction) (void))
113 {
114         SharedInvalidationMessage data;
115         int                     getResult;
116         bool            gotMessage = false;
117
118         for (;;)
119         {
120                 /*
121                  * We can discard any pending catchup event, since we will not exit
122                  * this loop until we're fully caught up.
123                  */
124                 catchupInterruptOccurred = 0;
125
126                 /*
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!
135                  */
136                 LWLockAcquire(SInvalLock, LW_SHARED);
137                 getResult = SIGetDataEntry(shmInvalBuffer, MyBackendId, &data);
138                 LWLockRelease(SInvalLock);
139
140                 if (getResult == 0)
141                         break;                          /* nothing more to do */
142                 if (getResult < 0)
143                 {
144                         /* got a reset message */
145                         elog(DEBUG4, "cache state reset");
146                         resetFunction();
147                 }
148                 else
149                 {
150                         /* got a normal data message */
151                         invalFunction(&data);
152                 }
153                 gotMessage = true;
154         }
155
156         /* If we got any messages, try to release dead messages */
157         if (gotMessage)
158         {
159                 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
160                 SIDelExpiredDataEntries(shmInvalBuffer);
161                 LWLockRelease(SInvalLock);
162         }
163 }
164
165
166 /*
167  * CatchupInterruptHandler
168  *
169  * This is the signal handler for SIGUSR1.
170  *
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.)
177  */
178 void
179 CatchupInterruptHandler(SIGNAL_ARGS)
180 {
181         int                     save_errno = errno;
182
183         /*
184          * Note: this is a SIGNAL HANDLER.      You must be very wary what you do
185          * here.
186          */
187
188         /* Don't joggle the elbow of proc_exit */
189         if (proc_exit_inprogress)
190                 return;
191
192         if (catchupInterruptEnabled)
193         {
194                 bool            save_ImmediateInterruptOK = ImmediateInterruptOK;
195
196                 /*
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.)
201                  */
202                 ImmediateInterruptOK = false;
203
204                 /*
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.
209                  */
210                 catchupInterruptEnabled = 0;    /* disable any recursive signal */
211                 catchupInterruptOccurred = 1;   /* do at least one iteration */
212                 for (;;)
213                 {
214                         catchupInterruptEnabled = 1;
215                         if (!catchupInterruptOccurred)
216                                 break;
217                         catchupInterruptEnabled = 0;
218                         if (catchupInterruptOccurred)
219                         {
220                                 /* Here, it is finally safe to do stuff. */
221                                 ProcessCatchupEvent();
222                         }
223                 }
224
225                 /*
226                  * Restore ImmediateInterruptOK, and check for interrupts if needed.
227                  */
228                 ImmediateInterruptOK = save_ImmediateInterruptOK;
229                 if (save_ImmediateInterruptOK)
230                         CHECK_FOR_INTERRUPTS();
231         }
232         else
233         {
234                 /*
235                  * In this path it is NOT SAFE to do much of anything, except this:
236                  */
237                 catchupInterruptOccurred = 1;
238         }
239
240         errno = save_errno;
241 }
242
243 /*
244  * EnableCatchupInterrupt
245  *
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.
249  *
250  * NOTE: the signal handler starts out disabled, and stays so until
251  * PostgresMain calls this the first time.
252  */
253 void
254 EnableCatchupInterrupt(void)
255 {
256         /*
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
272          * interrupt.
273          *
274          * NB: an overenthusiastic optimizing compiler could easily break this
275          * code. Hopefully, they all understand what "volatile" means these days.
276          */
277         for (;;)
278         {
279                 catchupInterruptEnabled = 1;
280                 if (!catchupInterruptOccurred)
281                         break;
282                 catchupInterruptEnabled = 0;
283                 if (catchupInterruptOccurred)
284                         ProcessCatchupEvent();
285         }
286 }
287
288 /*
289  * DisableCatchupInterrupt
290  *
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.
294  *
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.
298  */
299 bool
300 DisableCatchupInterrupt(void)
301 {
302         bool            result = (catchupInterruptEnabled != 0);
303
304         catchupInterruptEnabled = 0;
305
306         return result;
307 }
308
309 /*
310  * ProcessCatchupEvent
311  *
312  * Respond to a catchup event (SIGUSR1) from another backend.
313  *
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).
317  */
318 static void
319 ProcessCatchupEvent(void)
320 {
321         bool            notify_enabled;
322
323         /* Must prevent SIGUSR2 interrupt while I am running */
324         notify_enabled = DisableNotifyInterrupt();
325
326         /*
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.
333          *
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.
338          */
339         if (IsTransactionOrTransactionBlock())
340         {
341                 elog(DEBUG4, "ProcessCatchupEvent inside transaction");
342                 AcceptInvalidationMessages();
343         }
344         else
345         {
346                 elog(DEBUG4, "ProcessCatchupEvent outside transaction");
347                 StartTransactionCommand();
348                 CommitTransactionCommand();
349         }
350
351         if (notify_enabled)
352                 EnableNotifyInterrupt();
353 }