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
* 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,
{
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,
* 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;
}