mirror of
https://github.com/postgres/postgres.git
synced 2025-06-26 12:21:12 +03:00
Replace buffer I/O locks with condition variables.
1. Backends waiting for buffer I/O are now interruptible. 2. If something goes wrong in a backend that is currently performing I/O, waiting backends no longer wake up until that backend reaches AbortBufferIO() and broadcasts on the CV. Previously, any waiters would wake up (because the I/O lock was automatically released) and then busy-loop until AbortBufferIO() cleared BM_IO_IN_PROGRESS. 3. LWLockMinimallyPadded is removed, as it would now be unused. Author: Robert Haas <robertmhaas@gmail.com> Reviewed-by: Thomas Munro <thomas.munro@gmail.com> Reviewed-by: Julien Rouhaud <rjuju123@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> (earlier version, 2016) Discussion: https://postgr.es/m/CA%2BhUKGJ8nBFrjLuCTuqKN0pd2PQOwj9b_jnsiGFFMDvUxahj_A%40mail.gmail.com Discussion: https://postgr.es/m/CA+Tgmoaj2aPti0yho7FeEf2qt-JgQPRWb0gci_o1Hfr=C56Xng@mail.gmail.com
This commit is contained in:
@ -1352,9 +1352,10 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
|
||||
LWLockRelease(newPartitionLock);
|
||||
|
||||
/*
|
||||
* Buffer contents are currently invalid. Try to get the io_in_progress
|
||||
* lock. If StartBufferIO returns false, then someone else managed to
|
||||
* read it before we did, so there's nothing left for BufferAlloc() to do.
|
||||
* Buffer contents are currently invalid. Try to obtain the right to
|
||||
* start I/O. If StartBufferIO returns false, then someone else managed
|
||||
* to read it before we did, so there's nothing left for BufferAlloc() to
|
||||
* do.
|
||||
*/
|
||||
if (StartBufferIO(buf, true))
|
||||
*foundPtr = false;
|
||||
@ -1777,9 +1778,8 @@ UnpinBuffer(BufferDesc *buf, bool fixOwner)
|
||||
*/
|
||||
VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), BLCKSZ);
|
||||
|
||||
/* I'd better not still hold any locks on the buffer */
|
||||
/* I'd better not still hold the buffer content lock */
|
||||
Assert(!LWLockHeldByMe(BufferDescriptorGetContentLock(buf)));
|
||||
Assert(!LWLockHeldByMe(BufferDescriptorGetIOLock(buf)));
|
||||
|
||||
/*
|
||||
* Decrement the shared reference count.
|
||||
@ -2742,9 +2742,9 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
|
||||
uint32 buf_state;
|
||||
|
||||
/*
|
||||
* Acquire the buffer's io_in_progress lock. If StartBufferIO returns
|
||||
* false, then someone else flushed the buffer before we could, so we need
|
||||
* not do anything.
|
||||
* Try to start an I/O operation. If StartBufferIO returns false, then
|
||||
* someone else flushed the buffer before we could, so we need not do
|
||||
* anything.
|
||||
*/
|
||||
if (!StartBufferIO(buf, false))
|
||||
return;
|
||||
@ -2800,7 +2800,7 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
|
||||
/*
|
||||
* Now it's safe to write buffer to disk. Note that no one else should
|
||||
* have been able to write it while we were busy with log flushing because
|
||||
* we have the io_in_progress lock.
|
||||
* only one process at a time can set the BM_IO_IN_PROGRESS bit.
|
||||
*/
|
||||
bufBlock = BufHdrGetBlock(buf);
|
||||
|
||||
@ -2835,7 +2835,7 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
|
||||
|
||||
/*
|
||||
* Mark the buffer as clean (unless BM_JUST_DIRTIED has become set) and
|
||||
* end the io_in_progress state.
|
||||
* end the BM_IO_IN_PROGRESS state.
|
||||
*/
|
||||
TerminateBufferIO(buf, true, 0);
|
||||
|
||||
@ -4271,7 +4271,7 @@ IsBufferCleanupOK(Buffer buffer)
|
||||
* Functions for buffer I/O handling
|
||||
*
|
||||
* Note: We assume that nested buffer I/O never occurs.
|
||||
* i.e at most one io_in_progress lock is held per proc.
|
||||
* i.e at most one BM_IO_IN_PROGRESS bit is set per proc.
|
||||
*
|
||||
* Also note that these are used only for shared buffers, not local ones.
|
||||
*/
|
||||
@ -4282,13 +4282,9 @@ IsBufferCleanupOK(Buffer buffer)
|
||||
static void
|
||||
WaitIO(BufferDesc *buf)
|
||||
{
|
||||
/*
|
||||
* Changed to wait until there's no IO - Inoue 01/13/2000
|
||||
*
|
||||
* Note this is *necessary* because an error abort in the process doing
|
||||
* I/O could release the io_in_progress_lock prematurely. See
|
||||
* AbortBufferIO.
|
||||
*/
|
||||
ConditionVariable *cv = BufferDescriptorGetIOCV(buf);
|
||||
|
||||
ConditionVariablePrepareToSleep(cv);
|
||||
for (;;)
|
||||
{
|
||||
uint32 buf_state;
|
||||
@ -4303,9 +4299,9 @@ WaitIO(BufferDesc *buf)
|
||||
|
||||
if (!(buf_state & BM_IO_IN_PROGRESS))
|
||||
break;
|
||||
LWLockAcquire(BufferDescriptorGetIOLock(buf), LW_SHARED);
|
||||
LWLockRelease(BufferDescriptorGetIOLock(buf));
|
||||
ConditionVariableSleep(cv, WAIT_EVENT_BUFFER_IO);
|
||||
}
|
||||
ConditionVariableCancelSleep();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4317,7 +4313,7 @@ WaitIO(BufferDesc *buf)
|
||||
* In some scenarios there are race conditions in which multiple backends
|
||||
* could attempt the same I/O operation concurrently. If someone else
|
||||
* has already started I/O on this buffer then we will block on the
|
||||
* io_in_progress lock until he's done.
|
||||
* I/O condition variable until he's done.
|
||||
*
|
||||
* Input operations are only attempted on buffers that are not BM_VALID,
|
||||
* and output operations only on buffers that are BM_VALID and BM_DIRTY,
|
||||
@ -4335,25 +4331,11 @@ StartBufferIO(BufferDesc *buf, bool forInput)
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/*
|
||||
* Grab the io_in_progress lock so that other processes can wait for
|
||||
* me to finish the I/O.
|
||||
*/
|
||||
LWLockAcquire(BufferDescriptorGetIOLock(buf), LW_EXCLUSIVE);
|
||||
|
||||
buf_state = LockBufHdr(buf);
|
||||
|
||||
if (!(buf_state & BM_IO_IN_PROGRESS))
|
||||
break;
|
||||
|
||||
/*
|
||||
* The only way BM_IO_IN_PROGRESS could be set when the io_in_progress
|
||||
* lock isn't held is if the process doing the I/O is recovering from
|
||||
* an error (see AbortBufferIO). If that's the case, we must wait for
|
||||
* him to get unwedged.
|
||||
*/
|
||||
UnlockBufHdr(buf, buf_state);
|
||||
LWLockRelease(BufferDescriptorGetIOLock(buf));
|
||||
WaitIO(buf);
|
||||
}
|
||||
|
||||
@ -4363,7 +4345,6 @@ StartBufferIO(BufferDesc *buf, bool forInput)
|
||||
{
|
||||
/* someone else already did the I/O */
|
||||
UnlockBufHdr(buf, buf_state);
|
||||
LWLockRelease(BufferDescriptorGetIOLock(buf));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4381,7 +4362,6 @@ StartBufferIO(BufferDesc *buf, bool forInput)
|
||||
* (Assumptions)
|
||||
* My process is executing IO for the buffer
|
||||
* BM_IO_IN_PROGRESS bit is set for the buffer
|
||||
* We hold the buffer's io_in_progress lock
|
||||
* The buffer is Pinned
|
||||
*
|
||||
* If clear_dirty is true and BM_JUST_DIRTIED is not set, we clear the
|
||||
@ -4413,7 +4393,7 @@ TerminateBufferIO(BufferDesc *buf, bool clear_dirty, uint32 set_flag_bits)
|
||||
|
||||
InProgressBuf = NULL;
|
||||
|
||||
LWLockRelease(BufferDescriptorGetIOLock(buf));
|
||||
ConditionVariableBroadcast(BufferDescriptorGetIOCV(buf));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4434,14 +4414,6 @@ AbortBufferIO(void)
|
||||
{
|
||||
uint32 buf_state;
|
||||
|
||||
/*
|
||||
* Since LWLockReleaseAll has already been called, we're not holding
|
||||
* the buffer's io_in_progress_lock. We have to re-acquire it so that
|
||||
* we can use TerminateBufferIO. Anyone who's executing WaitIO on the
|
||||
* buffer will be in a busy spin until we succeed in doing this.
|
||||
*/
|
||||
LWLockAcquire(BufferDescriptorGetIOLock(buf), LW_EXCLUSIVE);
|
||||
|
||||
buf_state = LockBufHdr(buf);
|
||||
Assert(buf_state & BM_IO_IN_PROGRESS);
|
||||
if (IsForInput)
|
||||
|
Reference in New Issue
Block a user