From 710ea1e36cf0bed2dcf7e52b9339df28b453118e Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 22 Aug 2014 10:17:44 -0400 Subject: [PATCH] Broken LockCheckConflicts stuff. --- src/backend/storage/lmgr/lock.c | 119 ++++++++++++++++++++++++++++++++ src/include/storage/lock.h | 8 +++ 2 files changed, 127 insertions(+) diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index c754dd0af6..6c4f4a437e 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -1251,6 +1251,125 @@ RemoveLocalLock(LOCALLOCK *locallock) elog(WARNING, "locallock table corrupted"); } +/* + * CanGrantLock -- test for conflicts with already-granted locks, or with + * other waiters + */ +CanGrantLockResult +CanGrantLock(LockMethod lockMethodTable, LOCKMODE lockmode, LOCK *lock, + PROCLOCK *proclock) +{ + int numLockModes = lockMethodTable->numLockModes; + LOCKMASK conflictMask; + LOCKMASK myLocks; + int ngranted = 0; + int nawaited = 0; + bool group_member_waiting = false; + + /* + * If nobody's holding the lock, and nobody's waiting for the lock, + * then it's definitely OK to grant the lock. + */ + conflictMask = lockMethodTable->conflictTab[lockmode] & lock->waitMask; + if ((conflictMask & (lock->waitMask | lock->grantMask)) == 0) + return GRANT_OK; + + /* We can take some shortcuts when no group locking is involved. */ + if (proclock->groupLeader == NULL) + { + /* + * If no conflicting locks have been granted, there must at least + * be someone waiting for a conflicting lock, or the test above would + * have returned GRANT_OK. (Note that we can't use this fast-path + * when a locking group is in use, because the answer could still be + * GRANT_WAITERS_IN_GROUP.) + */ + if ((conflictMask & lock->grantMask) == 0) + return GRANT_WAITERS; + + /* + * With no lock group, it's only OK to ignore locks held by this + * proclock. Any other conflict must be for real. + */ + if (((conflictMask & lock->grantMask) & ~proclock->holdMask) != 0) + return GRANT_CONFLICT; + } + + /* + * There may be conflicting locks present, but they may belong to + * this proclock or some other proclock in its locking group. If not, + * there will at least be some awaited locks, which may or may not be + * in the same locking group. So we must do a more careful analysis. + */ + myLocks = proclock->holdMask; + for (i = 1; i <= numLockModes; i++) + { + /* If this lock type doesn't conflict, we can ignore it. */ + if ((conflictMask & LOCKBIT_ON(i)) == 0) + continue; + + /* + * Counted granted and awaited locks, excluding any held by our own + * proclock. + */ + ngranted += lock->granted[i] - ((myLocks & LOCKBIT_ON(i)) ? 1 : 0); + nawaited += lock->requested[i] - lock->granted[i]; + } + + /* + * If group locking is in use, we must account for locks held by other + * members of the locking group - unless of course the only locks held + * or awaited in a conflicting mode were held by this PROCLOCK, in + * which case we can skip all this. + */ + if (proclock->groupLeader != NULL && (ngranted > 0 || nawaited > 0)) + { + SHM_QUEUE *procLocks = &(lock->procLocks); + PROCLOCK *otherproclock; + + otherproclock = (PROCLOCK *) + SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, lockLink)); + + while (otherproclock != NULL) + { + if (proclock->groupLeader == otherproclock->groupLeader) + { + LOCKMASK conflicts; + + /* We can get any lock held by our group. */ + if ((otherproclock->holdMask & LOCKBIT_ON(lockmode)) == 0) + return GRANT_OK; + + /* We can disregard any conflicting lock held by our group. */ + conflicts = otherproclock->holdMask & conflictMask; + if (conflicts != 0) + { + for (i = 1; i <= numLockModes; i++) + { + if ((conflicts & LOCKBIT_ON(i)) != 0) + { + Assert(ngranted > 0); + ngranted--; + } + } + } + + If p awaits the sought mode, set group_member_waiting = true. + If p awaits a conflicting mode, then --w. REALLY???? + If g == 0 and w == 0, break. + } + } + } + + /* We can now render a final verdict. */ + if (ngranted > 0) + return GRANT_CONFLICT; + if (nawaited > 0) + return group_member_waiting ? GRANT_WAITERS_IN_GROUP : GRANT_WAITERS; + Assert(!group_member_waiting); + return GRANT_OK; +} + /* * LockCheckConflicts -- test whether requested lock conflicts * with those already granted diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index 33aece3927..ae477ae9eb 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -474,6 +474,14 @@ typedef enum * worker */ } DeadLockState; +/* Result codes for CanGrantLock */ +typedef enum +{ + GRANT_CONFLICT, /* conflicting locks exist */ + GRANT_WAITERS, /* no conflicts, some waiters, not in our group */ + GRANT_WAITERS_IN_GROUP, /* no conflicts, some waiters in our group */ + GRANT_OK, /* no conflicting locks, and no waiters */ +} CanGrantLockResult; /* * The lockmgr's shared hash tables are partitioned to reduce contention. -- 2.39.5