* that is reading in or writing out a page buffer does not hold the control
* lock, only the per-buffer lock for the buffer it is working on.
*
- * To change the page number or state of a buffer, one must normally hold
- * the control lock. (The sole exception to this rule is that a writer
- * process changes the state from DIRTY to WRITE_IN_PROGRESS while holding
- * only the per-buffer lock.) If the buffer's state is neither EMPTY nor
+ * To change the page number or state of a buffer, one must hold
+ * the control lock. If the buffer's state is neither EMPTY nor
* CLEAN, then there may be processes doing (or waiting to do) I/O on the
* buffer, so the page number may not be changed, and the only allowed state
* transition is to change WRITE_IN_PROGRESS to DIRTY after dirtying the page.
* the read, while the early marking prevents someone else from trying to
* read the same page into a different buffer.
*
- * Note we are assuming that read and write of the state value is atomic,
- * since I/O processes may examine and change the state while not holding
- * the control lock.
- *
* As with the regular buffer manager, it is possible for another process
* to re-dirty a page that is currently being written out. This is handled
* by setting the page's state from WRITE_IN_PROGRESS to DIRTY. The writing
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.28 2005/10/15 02:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.29 2005/11/03 00:23:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
SlruRecentlyUsed(shared, slotno);
- /* Release shared lock, grab per-buffer lock instead */
- LWLockRelease(shared->ControlLock);
- LWLockAcquire(shared->buffer_locks[slotno], LW_EXCLUSIVE);
+ /*
+ * We must grab the per-buffer lock to do I/O. To avoid deadlock,
+ * must release ControlLock while waiting for per-buffer lock.
+ * Fortunately, most of the time the per-buffer lock shouldn't be
+ * already held, so we can do this:
+ */
+ if (!LWLockConditionalAcquire(shared->buffer_locks[slotno],
+ LW_EXCLUSIVE))
+ {
+ LWLockRelease(shared->ControlLock);
+ LWLockAcquire(shared->buffer_locks[slotno], LW_EXCLUSIVE);
+ LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+ }
/*
* Check to see if someone else already did the read, or took the
shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS)
{
LWLockRelease(shared->buffer_locks[slotno]);
- LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
continue;
}
- /* Okay, do the read */
+ /* Okay, release control lock and do the read */
+ LWLockRelease(shared->ControlLock);
+
ok = SlruPhysicalReadPage(ctl, pageno, slotno);
/* Re-acquire shared control lock and update page state */
pageno = shared->page_number[slotno];
- /* Release shared lock, grab per-buffer lock instead */
- LWLockRelease(shared->ControlLock);
- LWLockAcquire(shared->buffer_locks[slotno], LW_EXCLUSIVE);
+ /*
+ * We must grab the per-buffer lock to do I/O. To avoid deadlock,
+ * must release ControlLock while waiting for per-buffer lock.
+ * Fortunately, most of the time the per-buffer lock shouldn't be
+ * already held, so we can do this:
+ */
+ if (!LWLockConditionalAcquire(shared->buffer_locks[slotno],
+ LW_EXCLUSIVE))
+ {
+ LWLockRelease(shared->ControlLock);
+ LWLockAcquire(shared->buffer_locks[slotno], LW_EXCLUSIVE);
+ LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+ }
/*
* Check to see if someone else already did the write, or took the buffer
shared->page_status[slotno] != SLRU_PAGE_WRITE_IN_PROGRESS))
{
LWLockRelease(shared->buffer_locks[slotno]);
- LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
return;
}
/*
* Mark the slot write-busy. After this point, a transaction status
- * update on this page will mark it dirty again. NB: we are assuming that
- * read/write of the page status field is atomic, since we change the
- * state while not holding control lock. However, we cannot set this
- * state any sooner, or we'd possibly fool a previous writer into thinking
- * he's successfully dumped the page when he hasn't. (Scenario: other
- * writer starts, page is redirtied, we come along and set
- * WRITE_IN_PROGRESS again, other writer completes and sets CLEAN because
- * redirty info has been lost, then we think it's clean too.)
+ * update on this page will mark it dirty again.
*/
shared->page_status[slotno] = SLRU_PAGE_WRITE_IN_PROGRESS;
- /* Okay, do the write */
+ /* Okay, release the control lock and do the write */
+ LWLockRelease(shared->ControlLock);
+
ok = SlruPhysicalWritePage(ctl, pageno, slotno, fdata);
/* If we failed, and we're in a flush, better close the files */
/*
* We need to do I/O. Normal case is that we have to write it out,
* but it's possible in the worst case to have selected a read-busy
- * page. In that case we use SimpleLruReadPage to wait for the read
- * to complete.
+ * page. In that case we just wait for someone else to complete
+ * the I/O, which we can do by waiting for the per-buffer lock.
*/
if (shared->page_status[bestslot] == SLRU_PAGE_READ_IN_PROGRESS)
- (void) SimpleLruReadPage(ctl, shared->page_number[bestslot],
- InvalidTransactionId);
+ {
+ LWLockRelease(shared->ControlLock);
+ LWLockAcquire(shared->buffer_locks[bestslot], LW_SHARED);
+ LWLockRelease(shared->buffer_locks[bestslot]);
+ LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+ }
else
SimpleLruWritePage(ctl, bestslot, NULL);
* the same logic as in SlruSelectLRUPage.
*/
if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS)
- (void) SimpleLruReadPage(ctl, shared->page_number[slotno],
- InvalidTransactionId);
+ {
+ LWLockRelease(shared->ControlLock);
+ LWLockAcquire(shared->buffer_locks[slotno], LW_SHARED);
+ LWLockRelease(shared->buffer_locks[slotno]);
+ LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+ }
else
SimpleLruWritePage(ctl, slotno, NULL);
goto restart;