在主锁表和进程锁表中保存锁之后,就可以进行锁的冲突检测。如果检测到当前申请的锁模式与其他事务已持有锁冲突,则必须进入等待状态。
一、 函数调用
冲突检测函数是LockCheckConflicts,其上层调用在我们之前提到过的LockAcquireExtended函数(当时我们只看了本地锁表相关代码,没有看全部)。
//lockMethodTable->conflictTab[lockmode]获取与待申请锁模式冲突的锁模式,lock->waitMask中保存在这个锁上等待的其他事务想要申请的锁模式。如果两者有交集(按位与结果为true),说明申请申请的锁模式与其他事务正在等待的锁模式冲突,必须进入等待状态。
if (lockMethodTable->conflictTab[lockmode] & lock->waitMask)
found_conflict = true;
// 如果没有其他事务等待这个锁,则检查当前申请的锁模式,是否与其他事务已持有的锁冲突
// if部分是检查其他事务等待队列,else部分是检查其他事务已持锁队列
else
found_conflict = LockCheckConflicts(lockMethodTable, lockmode,
lock, proclock);
二、 LockCheckConflicts函数
如上所述,LockCheckConflicts函数主要是检查当前申请的锁模式,是否与其他事务已持有的锁冲突。
/*
* LockCheckConflicts -- test whether requested lock conflicts with those already granted
* Returns true if conflict, false if no conflict.
*/
bool
LockCheckConflicts(LockMethod lockMethodTable,
LOCKMODE lockmode,
LOCK *lock,
PROCLOCK *proclock)
{
int numLockModes = lockMethodTable->numLockModes;
LOCKMASK myLocks;
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,
* then I get the lock.
*
* Checking for conflict: lock->grantMask represents the types of
* currently held locks. conflictTable[lockmode] has a bit set for each
* type of lock that conflicts with request. Bitwise compare tells if
* there is a conflict.
* lock->grantMask为其他事务当前已持有的锁模式,conflictMask为与待申请锁冲突的模式
*/
if (!(conflictMask & lock->grantMask)) //如果两者无交集,说明待申请的锁模式与其他事务已持有的锁模式均不冲突,可以直接获取
{
PROCLOCK_PRINT("LockCheckConflicts: no conflict", proclock);
return false; // Returns false if no conflict.
}
// proclock->holdMask 代表当前事务持有的该锁对象的某些模式
myLocks = proclock->holdMask;
// 循环分析8种锁模式
for (i = 1; i <= numLockModes; i++)
{
// 待申请的锁模式conflictMask 与 当前锁模式i 不冲突,跳过
if ((conflictMask & LOCKBIT_ON(i)) == 0)
{
conflictsRemaining[i] = 0;
continue;
}
// 记录当前锁模式i 在lock中被授予了多少次
conflictsRemaining[i] = lock->granted[i];
// 如果当前会话持有了这个锁对象的第i个锁模式i,减去,因为自己和自己不冲突
if (myLocks & LOCKBIT_ON(i))
--conflictsRemaining[i];
// 统计在所有锁模式上有多少事务持有这个锁,不包括自己
totalConflictsRemaining += conflictsRemaining[i];
}
/* If no conflicts remain, we get the lock. 如果没有事务持有这个锁,直接获得锁 */
if (totalConflictsRemaining == 0)
{
PROCLOCK_PRINT("LockCheckConflicts: resolved (simple)", proclock);
return false;
}
/* If no group locking, it's definitely a conflict. 如果是单进程事务,且totalConflictsRemaining不等于0,暂时不能获得锁 */
if (proclock->groupLeader == MyProc && MyProc->lockGroupLeader == NULL)
{
Assert(proclock->tag.myProc == MyProc);
PROCLOCK_PRINT("LockCheckConflicts: conflicting (simple)",
proclock);
return true;
}
/*
* The relation extension or page lock conflict even between the group members.
* relation extension锁或者页锁可能出现在多进程(并行执行的)组成员之间
*/
if (LOCK_LOCKTAG(*lock) == LOCKTAG_RELATION_EXTEND ||
(LOCK_LOCKTAG(*lock) == LOCKTAG_PAGE))
{
PROCLOCK_PRINT("LockCheckConflicts: conflicting (group)",
proclock);
return true;
}
/*
* 对于多进程(并行执行的)事务,其组成员持有的锁是不冲突的,因此要在totalConflictsRemaining减去与当前事务有相同group leader的事务计数。如果最终totalConflictsRemaining等于0,则可以获得锁,否则说明有冲突。
*/
procLocks = &(lock->procLocks);
otherproclock = (PROCLOCK *)
SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, lockLink));
while (otherproclock != NULL)
{
//判断其他进程与当前事务所在进程是否有相同的group leader
if (proclock != otherproclock &&
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 false;
}
}
otherproclock = (PROCLOCK *)
SHMQueueNext(procLocks, &otherproclock->lockLink,
offsetof(PROCLOCK, lockLink));
}
/* Nope, it's a real conflict. */
PROCLOCK_PRINT("LockCheckConflicts: conflicting (group)", proclock);
return true;
}
参考
《PostgreSQL技术内幕:事务处理深度探索》第2章