最近我一直在阅读 lock_manager (kern_lock.c) 代码,并遇到了一些我认为会产生竞争条件的场景。
步骤1:
@undo_shreq(...):如果有升级请求挂起,代码将重置“LKC_UPREQ”标志并调用wakeup();这件事只有在
(count & (LKC_EXREQ | LKC_UPREQ | LKC_CANCEL)) &&
(count & (LKC_SMASK | LKC_XMASK)) == 0)
第2步:
现在,并行地,另一个线程 T2 正试图获得一个独占锁,它达到了微不足道的条件(即)@lockmgr_exclusive(...)
if ((count & (LKC_UPREQ | LKC_EXREQ |
LKC_XMASK)) == 0 &&
((count & LKC_SHARED) == 0 ||
(count & LKC_SMASK) == 0))
因此,T2 将计数增加 1 并将自己设置为所有者线程——这意味着它获得了排他性。
第 3 步:
一个线程 (T1) 在 LKC_UPREQ 标志上休眠,由步骤 1 唤醒;这是睡眠后的代码(...之后,LK_SLEEPFAIL 和睡眠错误完整性检查),@lockmgr_upgrade(...)
if ((count & LKC_UPREQ) == 0) { // reset by step 1
KKASSERT((count & LKC_XMASK) == 1); // true, by step 2
lkp->lk_lockholder = td;
break;
}
我看到(如果错了请纠正我),在第 3 步,线程 T1 将 lk_lockholder 重置为自身——意思是,它获得了排他性!