最近,我一直在阅读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唤醒;以下是休眠后的代码(....after、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重置为自身--这意味着它获得了排他性!
发布于 2020-01-30 01:34:33
它的工作方式是,如果一个线程设置了UPREQ,然后休眠,另一个线程将授予它独占锁并唤醒它。授权线程清除UPREQ并递增独占计数,但是不知道是谁设置了UPREQ,因此接下来由设置UPREQ的线程来设置lockholder字段。
由于lockmgr代码必须同时处理多个独占到单个共享的饥饿、多个共享到单个独占的饥饿情况和死锁边缘情况,因此它相当复杂。在过去的一两年里,已经发现了边缘案例,但在我看来,这个特殊案例并不像是一个bug。只是有点混乱,因为排他锁是由第二个线程授予的(UPREQ清除,excl计数递增),但是第一线程仍然负责设置lk_lockholder字段。
https://stackoverflow.com/questions/59965974
复制相似问题