More hacking.
authorRobert Haas <rhaas@postgresql.org>
Thu, 2 Oct 2014 14:48:01 +0000 (10:48 -0400)
committerRobert Haas <rhaas@postgresql.org>
Thu, 2 Oct 2014 14:48:01 +0000 (10:48 -0400)
src/backend/storage/lmgr/lock.c
src/include/storage/lock.h

index 6c4f4a437e9a611bb297eefa35b9f52f1f016e93..c479a6e0545264fe992597c0285b959e06164864 100644 (file)
@@ -1251,125 +1251,6 @@ 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
@@ -1379,9 +1260,10 @@ CanGrantLock(LockMethod lockMethodTable, LOCKMODE lockmode, LOCK *lock,
  * NOTES:
  *             Here's what makes this complicated: one process's locks don't
  * conflict with one another, no matter what purpose they are held for
- * (eg, session and transaction locks do not conflict).
- * So, we must subtract off our own locks when determining whether the
- * requested new lock conflicts with those already held.
+ * (eg, session and transaction locks do not conflict).  Nor do the locks
+ * of one process in a lock group conflict with those of another process in
+ * the same group.  So, we must subtract off these locks when determining
+ * whether the requested new lock conflicts with those already held.
  */
 int
 LockCheckConflicts(LockMethod lockMethodTable,
@@ -1391,8 +1273,12 @@ LockCheckConflicts(LockMethod lockMethodTable,
 {
        int                     numLockModes = lockMethodTable->numLockModes;
        LOCKMASK        myLocks;
-       LOCKMASK        otherLocks;
+       int                     conflictMask = lockMethodTable->conflictTab[lockmode];
+       int                     conflictsRemaining[MAX_LOCKMODES];
+       int                     totalConflictsRemaining = 0;
        int                     i;
+       SHM_QUEUE  *procLocks;
+       PROCLOCK   *otherproclock;
 
        /*
         * first check for global conflicts: If no locks conflict with my request,
@@ -1403,40 +1289,98 @@ LockCheckConflicts(LockMethod lockMethodTable,
         * type of lock that conflicts with request.   Bitwise compare tells if
         * there is a conflict.
         */
-       if (!(lockMethodTable->conflictTab[lockmode] & lock->grantMask))
+       if (!(conflictMask & lock->grantMask))
        {
                PROCLOCK_PRINT("LockCheckConflicts: no conflict", proclock);
                return STATUS_OK;
        }
 
        /*
-        * Rats.  Something conflicts.  But it could still be my own lock. We have
-        * to construct a conflict mask that does not reflect our own locks, but
-        * only lock types held by other processes.
+        * Rats.  Something conflicts.  But it could still be my own lock, or
+        * a lock held by another member of my locking group.  First, figure out
+        * how many conflicts remain after subtracting out any locks I hold
+        * myself.
         */
        myLocks = proclock->holdMask;
-       otherLocks = 0;
        for (i = 1; i <= numLockModes; i++)
        {
-               int                     myHolding = (myLocks & LOCKBIT_ON(i)) ? 1 : 0;
+               if ((conflictMask & LOCKBIT_ON(i)) == 0)
+               {
+                       conflictsRemaining[i] = 0;
+                       continue;
+               }
+               conflictsRemaining[i] = lock->granted[i];
+               if (myLocks & LOCKBIT_ON(i))
+                       --conflictsRemaining[i];
+               totalConflictsRemaining += conflictsRemaining[i];
+       }
 
-               if (lock->granted[i] > myHolding)
-                       otherLocks |= LOCKBIT_ON(i);
+       /* If no conflicts remain, we get the lock. */
+       if (totalConflictsRemaining == 0)
+       {
+               PROCLOCK_PRINT("LockCheckConflicts: resolved (simple)", proclock);
+               return STATUS_OK;
+       }
+
+       /* If we're not using group locking, this is definitely a conflict. */
+       if (proclock->groupLeader == NULL)
+       {
+               PROCLOCK_PRINT("LockCheckConflicts: conflicting (simple)", proclock);
+               return STATUS_FOUND;
+       }
+
+       /* Important special case: we're the only member of a lock group. */
+       if (proclock->groupLeader == MyProc && MyProc->lockGroupMembers < 2)
+       {
+               Assert(proclock->tag.myProc == MyProc);
+               Assert(MyProc->lockGroupMembers == 1);
+               PROCLOCK_PRINT("LockCheckConflicts: conflicting (trivial group)",
+                                          proclock);
+               return STATUS_FOUND;
        }
 
        /*
-        * now check again for conflicts.  'otherLocks' describes the types of
-        * locks held by other processes.  If one of these conflicts with the kind
-        * of lock that I want, there is a conflict and I have to sleep.
+        * Locks held in conflicting modes by members of our own lock group are
+        * not real conflicts; we can subtract those out and see if we still have
+        * a conflict.  This is O(N) in the number of processes holding or awaiting
+        * locks on this object.  We could improve that by making the shared memory
+        * state more complex (and larger) but it doesn't seem worth it.
         */
-       if (!(lockMethodTable->conflictTab[lockmode] & otherLocks))
+       procLocks = &(lock->procLocks);
+       otherproclock = (PROCLOCK *)
+               SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, lockLink));
+       while (otherproclock != NULL)
        {
-               /* no conflict. OK to get the lock */
-               PROCLOCK_PRINT("LockCheckConflicts: resolved", proclock);
-               return STATUS_OK;
+               if (proclock->groupLeader == otherproclock->groupLeader &&
+                       (otherproclock->holdMask & conflictMask) != 0)
+               {
+                       int     intersectMask = otherproclock->holdMask & conflictMask;
+
+                       for (i = 1; i <= numLockModes; i++)
+                       {
+                               if ((intersectMask & LOCKBIT_ON(i)) != 0)
+                               {
+                                       if (conflictsRemaining[i] <= 0)
+                                               elog(PANIC, "proclocks held do not match lock");
+                                       conflictsRemaining[i]--;
+                                       totalConflictsRemaining--;
+                               }
+                       }
+
+                       if (totalConflictsRemaining == 0)
+                       {
+                               PROCLOCK_PRINT("LockCheckConflicts: resolved (group)",
+                                                          proclock);
+                               return STATUS_OK;
+                       }
+               }
+               otherproclock = (PROCLOCK *)
+                       SHMQueueNext(procLocks, &proclock->lockLink,
+                                                offsetof(PROCLOCK, lockLink));
        }
 
-       PROCLOCK_PRINT("LockCheckConflicts: conflicting", proclock);
+       /* Nope, it's a real conflict. */
+       PROCLOCK_PRINT("LockCheckConflicts: conflicting (group)", proclock);
        return STATUS_FOUND;
 }
 
index ae477ae9eba47141ed6866e160b36893a03f9428..b15addb270f399515b528dcb3c56765622414539 100644 (file)
@@ -474,15 +474,6 @@ 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.
  * To determine which partition a given locktag belongs to, compute the tag's