Broken LockCheckConflicts stuff.
authorRobert Haas <[email protected]>
Fri, 22 Aug 2014 14:17:44 +0000 (10:17 -0400)
committerRobert Haas <[email protected]>
Fri, 22 Aug 2014 14:17:44 +0000 (10:17 -0400)
src/backend/storage/lmgr/lock.c
src/include/storage/lock.h

index c754dd0af645120f04bb21c0ec786f0ddd153fcb..6c4f4a437e9a611bb297eefa35b9f52f1f016e93 100644 (file)
@@ -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
index 33aece3927fbfb295cf767d734d1e1612af91b7e..ae477ae9eba47141ed6866e160b36893a03f9428 100644 (file)
@@ -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.