]> granicus.if.org Git - postgresql/blob - src/backend/storage/ipc/sinval.c
Modify interactions between sinval.c and sinvaladt.c. The code that actually
[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.84 2008/03/16 19:47:33 alvherre 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  * SendSharedInvalidMessage
53  *      Add a shared-cache-invalidation message to the global SI message queue.
54  */
55 void
56 SendSharedInvalidMessage(SharedInvalidationMessage *msg)
57 {
58         bool            insertOK;
59
60         insertOK = SIInsertDataEntry(msg);
61         if (!insertOK)
62                 elog(DEBUG4, "SI buffer overflow");
63 }
64
65 /*
66  * ReceiveSharedInvalidMessages
67  *              Process shared-cache-invalidation messages waiting for this backend
68  *
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.
74  */
75 void
76 ReceiveSharedInvalidMessages(
77                                           void (*invalFunction) (SharedInvalidationMessage *msg),
78                                                          void (*resetFunction) (void))
79 {
80         SharedInvalidationMessage data;
81         int                     getResult;
82         bool            gotMessage = false;
83
84         for (;;)
85         {
86                 /*
87                  * We can discard any pending catchup event, since we will not exit
88                  * this loop until we're fully caught up.
89                  */
90                 catchupInterruptOccurred = 0;
91
92                 getResult = SIGetDataEntry(MyBackendId, &data);
93
94                 if (getResult == 0)
95                         break;                          /* nothing more to do */
96                 if (getResult < 0)
97                 {
98                         /* got a reset message */
99                         elog(DEBUG4, "cache state reset");
100                         resetFunction();
101                 }
102                 else
103                 {
104                         /* got a normal data message */
105                         invalFunction(&data);
106                 }
107                 gotMessage = true;
108         }
109
110         /* If we got any messages, try to release dead messages */
111         if (gotMessage)
112                 SIDelExpiredDataEntries(false);
113 }
114
115
116 /*
117  * CatchupInterruptHandler
118  *
119  * This is the signal handler for SIGUSR1.
120  *
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.)
127  */
128 void
129 CatchupInterruptHandler(SIGNAL_ARGS)
130 {
131         int                     save_errno = errno;
132
133         /*
134          * Note: this is a SIGNAL HANDLER.      You must be very wary what you do
135          * here.
136          */
137
138         /* Don't joggle the elbow of proc_exit */
139         if (proc_exit_inprogress)
140                 return;
141
142         if (catchupInterruptEnabled)
143         {
144                 bool            save_ImmediateInterruptOK = ImmediateInterruptOK;
145
146                 /*
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.)
151                  */
152                 ImmediateInterruptOK = false;
153
154                 /*
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.
159                  */
160                 catchupInterruptEnabled = 0;    /* disable any recursive signal */
161                 catchupInterruptOccurred = 1;   /* do at least one iteration */
162                 for (;;)
163                 {
164                         catchupInterruptEnabled = 1;
165                         if (!catchupInterruptOccurred)
166                                 break;
167                         catchupInterruptEnabled = 0;
168                         if (catchupInterruptOccurred)
169                         {
170                                 /* Here, it is finally safe to do stuff. */
171                                 ProcessCatchupEvent();
172                         }
173                 }
174
175                 /*
176                  * Restore ImmediateInterruptOK, and check for interrupts if needed.
177                  */
178                 ImmediateInterruptOK = save_ImmediateInterruptOK;
179                 if (save_ImmediateInterruptOK)
180                         CHECK_FOR_INTERRUPTS();
181         }
182         else
183         {
184                 /*
185                  * In this path it is NOT SAFE to do much of anything, except this:
186                  */
187                 catchupInterruptOccurred = 1;
188         }
189
190         errno = save_errno;
191 }
192
193 /*
194  * EnableCatchupInterrupt
195  *
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.
199  *
200  * NOTE: the signal handler starts out disabled, and stays so until
201  * PostgresMain calls this the first time.
202  */
203 void
204 EnableCatchupInterrupt(void)
205 {
206         /*
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
222          * interrupt.
223          *
224          * NB: an overenthusiastic optimizing compiler could easily break this
225          * code. Hopefully, they all understand what "volatile" means these days.
226          */
227         for (;;)
228         {
229                 catchupInterruptEnabled = 1;
230                 if (!catchupInterruptOccurred)
231                         break;
232                 catchupInterruptEnabled = 0;
233                 if (catchupInterruptOccurred)
234                         ProcessCatchupEvent();
235         }
236 }
237
238 /*
239  * DisableCatchupInterrupt
240  *
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.
244  *
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.
248  */
249 bool
250 DisableCatchupInterrupt(void)
251 {
252         bool            result = (catchupInterruptEnabled != 0);
253
254         catchupInterruptEnabled = 0;
255
256         return result;
257 }
258
259 /*
260  * ProcessCatchupEvent
261  *
262  * Respond to a catchup event (SIGUSR1) from another backend.
263  *
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).
267  */
268 static void
269 ProcessCatchupEvent(void)
270 {
271         bool            notify_enabled;
272
273         /* Must prevent SIGUSR2 interrupt while I am running */
274         notify_enabled = DisableNotifyInterrupt();
275
276         /*
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.
283          *
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.
288          */
289         if (IsTransactionOrTransactionBlock())
290         {
291                 elog(DEBUG4, "ProcessCatchupEvent inside transaction");
292                 AcceptInvalidationMessages();
293         }
294         else
295         {
296                 elog(DEBUG4, "ProcessCatchupEvent outside transaction");
297                 StartTransactionCommand();
298                 CommitTransactionCommand();
299         }
300
301         if (notify_enabled)
302                 EnableNotifyInterrupt();
303 }