最近我一直在阅读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)睡在步骤1唤醒的LKC_UPREQ标志上;这是睡眠后的代码(....之后,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重置为其自身-意味着,它具有排他性!
最近,我一直在阅读lock_manager(kern_lock.c)代码,并遇到了一些我认为会产生竞争条件的情况。步骤1:@undo_shreq(...):如果有升级请求...
其工作方式是,如果一个线程设置UPREQ然后进入睡眠状态,则另一个线程将授予它排他锁并将其唤醒。授予线程清除UPREQ并增加独占计数,但是不知道“谁”设置了UPREQ,因此由设置UPREQ来设置锁持有者字段的线程决定。
由于lockmgr代码必须同时处理许多独占的饥饿,许多独占的饥饿以及死锁边缘情况,所以它相当复杂。在过去的一两年中发现了一些边缘案例,但是对我来说,这个特殊案例似乎并不像个bug。有点令人困惑,因为第二个线程授予了排他锁(UPREQ已清除,并且excl计数递增),但是第一个线程仍负责设置lk_lockholder字段。