|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.62 2003/11/29 19:51:56 pgsql Exp $ |
| 11 | + * $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.63 2004/05/23 03:50:45 tgl Exp $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
15 | 15 | #include"postgres.h"
|
16 | 16 |
|
| 17 | +#include<signal.h> |
17 | 18 |
|
| 19 | +#include"commands/async.h" |
| 20 | +#include"storage/ipc.h" |
18 | 21 | #include"storage/proc.h"
|
19 | 22 | #include"storage/sinval.h"
|
20 | 23 | #include"storage/sinvaladt.h"
|
| 24 | +#include"utils/inval.h" |
21 | 25 | #include"utils/tqual.h"
|
22 | 26 | #include"miscadmin.h"
|
23 | 27 |
|
24 | 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 | +staticvolatileintcatchupInterruptEnabled=0; |
| 46 | +staticvolatileintcatchupInterruptOccurred=0; |
| 47 | + |
| 48 | +staticvoidProcessCatchupEvent(void); |
| 49 | + |
| 50 | + |
25 | 51 | /****************************************************************************/
|
26 | 52 | /*CreateSharedInvalidationState() Initialize SI buffer*/
|
27 | 53 | /**/
|
@@ -91,6 +117,12 @@ ReceiveSharedInvalidMessages(
|
91 | 117 |
|
92 | 118 | for (;;)
|
93 | 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 | + |
94 | 126 | /*
|
95 | 127 | * We can run SIGetDataEntry in parallel with other backends
|
96 | 128 | * running SIGetDataEntry for themselves, since each instance will
|
@@ -137,6 +169,203 @@ ReceiveSharedInvalidMessages(
|
137 | 169 | }
|
138 | 170 |
|
139 | 171 |
|
| 172 | +/* |
| 173 | + * CatchupInterruptHandler |
| 174 | + * |
| 175 | + * This is the signal handler for SIGUSR1. |
| 176 | + * |
| 177 | + * If we are idle (catchupInterruptEnabled is set), we can safely |
| 178 | + * invoke ProcessCatchupEvent directly. Otherwise, just set a flag |
| 179 | + * to do it later. (Note that it's quite possible for normal processing |
| 180 | + * of the current transaction to cause ReceiveSharedInvalidMessages() |
| 181 | + * to be run later on; in that case the flag will get cleared again, |
| 182 | + * since there's no longer any reason to do anything.) |
| 183 | + */ |
| 184 | +void |
| 185 | +CatchupInterruptHandler(SIGNAL_ARGS) |
| 186 | +{ |
| 187 | +intsave_errno=errno; |
| 188 | + |
| 189 | +/* |
| 190 | + * Note: this is a SIGNAL HANDLER.You must be very wary what you do |
| 191 | + * here. |
| 192 | + */ |
| 193 | + |
| 194 | +/* Don't joggle the elbow of proc_exit */ |
| 195 | +if (proc_exit_inprogress) |
| 196 | +return; |
| 197 | + |
| 198 | +if (catchupInterruptEnabled) |
| 199 | +{ |
| 200 | +boolsave_ImmediateInterruptOK=ImmediateInterruptOK; |
| 201 | + |
| 202 | +/* |
| 203 | + * We may be called while ImmediateInterruptOK is true; turn it |
| 204 | + * off while messing with the catchup state. (We would have to |
| 205 | + * save and restore it anyway, because PGSemaphore operations |
| 206 | + * inside ProcessCatchupEvent() might reset it.) |
| 207 | + */ |
| 208 | +ImmediateInterruptOK= false; |
| 209 | + |
| 210 | +/* |
| 211 | + * I'm not sure whether some flavors of Unix might allow another |
| 212 | + * SIGUSR1 occurrence to recursively interrupt this routine. To |
| 213 | + * cope with the possibility, we do the same sort of dance that |
| 214 | + * EnableCatchupInterrupt must do --- see that routine for |
| 215 | + * comments. |
| 216 | + */ |
| 217 | +catchupInterruptEnabled=0;/* disable any recursive signal */ |
| 218 | +catchupInterruptOccurred=1;/* do at least one iteration */ |
| 219 | +for (;;) |
| 220 | +{ |
| 221 | +catchupInterruptEnabled=1; |
| 222 | +if (!catchupInterruptOccurred) |
| 223 | +break; |
| 224 | +catchupInterruptEnabled=0; |
| 225 | +if (catchupInterruptOccurred) |
| 226 | +{ |
| 227 | +/* Here, it is finally safe to do stuff. */ |
| 228 | +ProcessCatchupEvent(); |
| 229 | +} |
| 230 | +} |
| 231 | + |
| 232 | +/* |
| 233 | + * Restore ImmediateInterruptOK, and check for interrupts if |
| 234 | + * needed. |
| 235 | + */ |
| 236 | +ImmediateInterruptOK=save_ImmediateInterruptOK; |
| 237 | +if (save_ImmediateInterruptOK) |
| 238 | +CHECK_FOR_INTERRUPTS(); |
| 239 | +} |
| 240 | +else |
| 241 | +{ |
| 242 | +/* |
| 243 | + * In this path it is NOT SAFE to do much of anything, except |
| 244 | + * this: |
| 245 | + */ |
| 246 | +catchupInterruptOccurred=1; |
| 247 | +} |
| 248 | + |
| 249 | +errno=save_errno; |
| 250 | +} |
| 251 | + |
| 252 | +/* |
| 253 | + * EnableCatchupInterrupt |
| 254 | + * |
| 255 | + * This is called by the PostgresMain main loop just before waiting |
| 256 | + * for a frontend command. We process any pending catchup events, |
| 257 | + * and enable the signal handler to process future events directly. |
| 258 | + * |
| 259 | + * NOTE: the signal handler starts out disabled, and stays so until |
| 260 | + * PostgresMain calls this the first time. |
| 261 | + */ |
| 262 | +void |
| 263 | +EnableCatchupInterrupt(void) |
| 264 | +{ |
| 265 | +/* |
| 266 | + * This code is tricky because we are communicating with a signal |
| 267 | + * handler that could interrupt us at any point. If we just checked |
| 268 | + * catchupInterruptOccurred and then set catchupInterruptEnabled, we |
| 269 | + * could fail to respond promptly to a signal that happens in between |
| 270 | + * those two steps. (A very small time window, perhaps, but Murphy's |
| 271 | + * Law says you can hit it...)Instead, we first set the enable flag, |
| 272 | + * then test the occurred flag. If we see an unserviced interrupt has |
| 273 | + * occurred, we re-clear the enable flag before going off to do the |
| 274 | + * service work. (That prevents re-entrant invocation of |
| 275 | + * ProcessCatchupEvent() if another interrupt occurs.) If an |
| 276 | + * interrupt comes in between the setting and clearing of |
| 277 | + * catchupInterruptEnabled, then it will have done the service work and |
| 278 | + * left catchupInterruptOccurred zero, so we have to check again after |
| 279 | + * clearing enable. The whole thing has to be in a loop in case |
| 280 | + * another interrupt occurs while we're servicing the first. Once we |
| 281 | + * get out of the loop, enable is set and we know there is no |
| 282 | + * unserviced interrupt. |
| 283 | + * |
| 284 | + * NB: an overenthusiastic optimizing compiler could easily break this |
| 285 | + * code. Hopefully, they all understand what "volatile" means these |
| 286 | + * days. |
| 287 | + */ |
| 288 | +for (;;) |
| 289 | +{ |
| 290 | +catchupInterruptEnabled=1; |
| 291 | +if (!catchupInterruptOccurred) |
| 292 | +break; |
| 293 | +catchupInterruptEnabled=0; |
| 294 | +if (catchupInterruptOccurred) |
| 295 | +{ |
| 296 | +ProcessCatchupEvent(); |
| 297 | +} |
| 298 | +} |
| 299 | +} |
| 300 | + |
| 301 | +/* |
| 302 | + * DisableCatchupInterrupt |
| 303 | + * |
| 304 | + * This is called by the PostgresMain main loop just after receiving |
| 305 | + * a frontend command. Signal handler execution of catchup events |
| 306 | + * is disabled until the next EnableCatchupInterrupt call. |
| 307 | + * |
| 308 | + * The SIGUSR2 signal handler also needs to call this, so as to |
| 309 | + * prevent conflicts if one signal interrupts the other. So we |
| 310 | + * must return the previous state of the flag. |
| 311 | + */ |
| 312 | +bool |
| 313 | +DisableCatchupInterrupt(void) |
| 314 | +{ |
| 315 | +boolresult= (catchupInterruptEnabled!=0); |
| 316 | + |
| 317 | +catchupInterruptEnabled=0; |
| 318 | + |
| 319 | +returnresult; |
| 320 | +} |
| 321 | + |
| 322 | +/* |
| 323 | + * ProcessCatchupEvent |
| 324 | + * |
| 325 | + * Respond to a catchup event (SIGUSR1) from another backend. |
| 326 | + * |
| 327 | + * This is called either directly from the SIGUSR1 signal handler, |
| 328 | + * or the next time control reaches the outer idle loop (assuming |
| 329 | + * there's still anything to do by then). |
| 330 | + */ |
| 331 | +staticvoid |
| 332 | +ProcessCatchupEvent(void) |
| 333 | +{ |
| 334 | +boolnotify_enabled; |
| 335 | + |
| 336 | +/* Must prevent SIGUSR2 interrupt while I am running */ |
| 337 | +notify_enabled=DisableNotifyInterrupt(); |
| 338 | + |
| 339 | +/* |
| 340 | + * What we need to do here is cause ReceiveSharedInvalidMessages() |
| 341 | + * to run, which will do the necessary work and also reset the |
| 342 | + * catchupInterruptOccurred flag. If we are inside a transaction |
| 343 | + * we can just call AcceptInvalidationMessages() to do this. If we |
| 344 | + * aren't, we start and immediately end a transaction; the call to |
| 345 | + * AcceptInvalidationMessages() happens down inside transaction start. |
| 346 | + * |
| 347 | + * It is awfully tempting to just call AcceptInvalidationMessages() |
| 348 | + * without the rest of the xact start/stop overhead, and I think that |
| 349 | + * would actually work in the normal case; but I am not sure that things |
| 350 | + * would clean up nicely if we got an error partway through. |
| 351 | + */ |
| 352 | +if (IsTransactionOrTransactionBlock()) |
| 353 | +{ |
| 354 | +elog(DEBUG4,"ProcessCatchupEvent inside transaction"); |
| 355 | +AcceptInvalidationMessages(); |
| 356 | +} |
| 357 | +else |
| 358 | +{ |
| 359 | +elog(DEBUG4,"ProcessCatchupEvent outside transaction"); |
| 360 | +StartTransactionCommand(); |
| 361 | +CommitTransactionCommand(); |
| 362 | +} |
| 363 | + |
| 364 | +if (notify_enabled) |
| 365 | +EnableNotifyInterrupt(); |
| 366 | +} |
| 367 | + |
| 368 | + |
140 | 369 | /****************************************************************************/
|
141 | 370 | /* Functions that need to scan the PGPROC structures of all running backends. */
|
142 | 371 | /* It's a bit strange to keep these in sinval.c, since they don't have any*/
|
|