Skip to content

Commit dc02a48

Browse files
committed
Fix a race condition that I introduced into sinvaladt.c during the recent
rewrite. When called from SIInsertDataEntries, SICleanupQueue releases the write lock if it has to issue a kill() to signal some laggard backend. That still seems like a good idea --- but it's possible that by the time we get the lock back, there are no longer enough free message slots to satisfy SIInsertDataEntries' requirement. Must recheck, and repeat the whole SICleanupQueue process if not. Noted while reading code.
1 parent a4775a8 commit dc02a48

File tree

1 file changed

+17
-11
lines changed

1 file changed

+17
-11
lines changed

src/backend/storage/ipc/sinvaladt.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.73 2008/07/01 02:09:34 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.74 2008/07/18 14:45:48 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -296,8 +296,6 @@ SharedInvalBackendInit(void)
296296

297297
MyBackendId = (stateP - &segP->procState[0]) + 1;
298298

299-
elog(DEBUG4, "my backend id is %d", MyBackendId);
300-
301299
/* Advertise assigned backend ID in MyProc */
302300
MyProc->backendId = MyBackendId;
303301

@@ -314,6 +312,8 @@ SharedInvalBackendInit(void)
314312

315313
/* register exit routine to mark my entry inactive at exit */
316314
on_shmem_exit(CleanupInvalidationState, PointerGetDatum(segP));
315+
316+
elog(DEBUG4, "my backend id is %d", MyBackendId);
317317
}
318318

319319
/*
@@ -416,16 +416,18 @@ SIInsertDataEntries(const SharedInvalidationMessage *data, int n)
416416
* If the buffer is full, we *must* acquire some space. Clean the
417417
* queue and reset anyone who is preventing space from being freed.
418418
* Otherwise, clean the queue only when it's exceeded the next
419-
* fullness threshold.
419+
* fullness threshold. We have to loop and recheck the buffer state
420+
* after any call of SICleanupQueue.
420421
*/
421-
numMsgs = segP->maxMsgNum - segP->minMsgNum;
422-
if (numMsgs + nthistime > MAXNUMMESSAGES)
422+
for (;;)
423423
{
424-
SICleanupQueue(true, nthistime);
425-
Assert((segP->maxMsgNum - segP->minMsgNum + nthistime) <= MAXNUMMESSAGES);
424+
numMsgs = segP->maxMsgNum - segP->minMsgNum;
425+
if (numMsgs + nthistime > MAXNUMMESSAGES ||
426+
numMsgs >= segP->nextThreshold)
427+
SICleanupQueue(true, nthistime);
428+
else
429+
break;
426430
}
427-
else if (numMsgs >= segP->nextThreshold)
428-
SICleanupQueue(true, 0);
429431

430432
/*
431433
* Insert new message(s) into proper slot of circular buffer
@@ -546,12 +548,16 @@ SIGetDataEntries(SharedInvalidationMessage *data, int datasize)
546548
* Remove messages that have been consumed by all active backends
547549
*
548550
* callerHasWriteLock is TRUE if caller is holding SInvalWriteLock.
549-
* minFree is the minimum number of free message slots required at completion.
551+
* minFree is the minimum number of message slots to make free.
550552
*
551553
* Possible side effects of this routine include marking one or more
552554
* backends as "reset" in the array, and sending a catchup interrupt (SIGUSR1)
553555
* to some backend that seems to be getting too far behind. We signal at
554556
* most one backend at a time, for reasons explained at the top of the file.
557+
*
558+
* Caution: because we transiently release write lock when we have to signal
559+
* some other backend, it is NOT guaranteed that there are still minFree
560+
* free message slots at exit. Caller must recheck and perhaps retry.
555561
*/
556562
void
557563
SICleanupQueue(bool callerHasWriteLock, int minFree)

0 commit comments

Comments
 (0)