diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index 5453549..270af0a 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -482,6 +482,7 @@ static inline bool LWLockAcquireCommon(LWLock *l, LWLockMode mode, uint64 *valptr, uint64 val) { volatile LWLock *lock = l; + volatile uint64 *valp = valptr; PGPROC *proc = MyProc; bool retry = false; bool result = true; @@ -637,8 +638,8 @@ LWLockAcquireCommon(LWLock *l, LWLockMode mode, uint64 *valptr, uint64 val) } /* If there's a variable associated with this lock, initialize it */ - if (valptr) - *valptr = val; + if (valp) + *valp = val; /* We are done updating shared state of the lock itself. */ SpinLockRelease(&lock->mutex); @@ -1111,6 +1112,7 @@ LWLockRelease(LWLock *l) { volatile LWLock *lock = l; PGPROC *head; + PGPROC *tail = NULL; PGPROC *proc; int i; @@ -1145,63 +1147,70 @@ LWLockRelease(LWLock *l) /* * See if I need to awaken any waiters. If I released a non-last shared - * hold, there cannot be anything to do. Also, do not awaken any waiters - * if someone has already awakened waiters that haven't yet acquired the - * lock. + * hold, there cannot be anything to do. */ head = lock->head; - if (head != NULL) + if (head != NULL && lock->exclusive == 0 && lock->shared == 0) { - if (lock->exclusive == 0 && lock->shared == 0 && lock->releaseOK) - { - /* - * Remove the to-be-awakened PGPROCs from the queue. - */ - bool releaseOK = true; - - proc = head; + proc = head; - /* - * First wake up any backends that want to be woken up without - * acquiring the lock. - */ - while (proc->lwWaitMode == LW_WAIT_UNTIL_FREE && proc->lwWaitLink) - proc = proc->lwWaitLink; + /* + * First wake up any backends that want to be woken up without + * acquiring the lock. They are woken up regardless of releaseOK. + */ + while (proc && proc->lwWaitMode == LW_WAIT_UNTIL_FREE) + { + tail = proc; + proc = proc->lwWaitLink; + } - /* - * If the front waiter wants exclusive lock, awaken him only. - * Otherwise awaken as many waiters as want shared access. - */ - if (proc->lwWaitMode != LW_EXCLUSIVE) + /* + * If the front waiter (after any LW_WAIT_UNTIL_FREE waiters) wants + * exclusive lock, awaken him only. Otherwise awaken as many waiters + * as want shared access. But do not awaken any waiters if someone has + * already awakened waiters that haven't yet acquired the lock. + */ + if (proc && lock->releaseOK) + { + if (proc->lwWaitMode == LW_EXCLUSIVE) + tail = proc; + else { - while (proc->lwWaitLink != NULL && - proc->lwWaitLink->lwWaitMode != LW_EXCLUSIVE) + while (proc && proc->lwWaitMode != LW_EXCLUSIVE) { - if (proc->lwWaitMode != LW_WAIT_UNTIL_FREE) - releaseOK = false; + tail = proc; proc = proc->lwWaitLink; } } - /* proc is now the last PGPROC to be released */ - lock->head = proc->lwWaitLink; - proc->lwWaitLink = NULL; + } + /* + * 'tail' is now the last PGPROC to be released. Unlink the + * to-be-awakened PGPROCs from the queue. + */ + if (tail) + { + lock->head = tail->lwWaitLink; + tail->lwWaitLink = NULL; /* * Prevent additional wakeups until retryer gets to run. Backends * that are just waiting for the lock to become free don't retry * automatically. */ - if (proc->lwWaitMode != LW_WAIT_UNTIL_FREE) - releaseOK = false; - - lock->releaseOK = releaseOK; + if (tail->lwWaitMode != LW_WAIT_UNTIL_FREE) + lock->releaseOK = false; } else { - /* lock is still held, can't awaken anything */ + /* didn't wake up anyone after all */ head = NULL; } } + else + { + /* lock is still held, can't awaken anything */ + head = NULL; + } /* We are done updating shared state of the lock itself. */ SpinLockRelease(&lock->mutex);