DragonflyBSD:锁管理器(kern_lock.c)代码中可能存在竞争条件?

DragonflyBSD: possible race-condition in lock manager (kern_lock.c) code?

最近我一直在阅读 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 重置为其自身——意思是,它获得了独占性!

它的工作方式是,如果一个线程设置UPREQ然后休眠,另一个线程将授予它独占锁并将其唤醒。授予线程清除 UPREQ 并增加独占计数,但不知道 'who' 设置 UPREQ,因此由设置 UPREQ 的线程设置锁持有者字段。

由于 lockmgr 代码必须同时处理多独占到单共享饥饿、多共享到单独占饥饿情况以及死锁边缘情况,因此它相当复杂。在过去的一两年里发现了边缘案例,但这个特殊案例对我来说看起来不像是错误。只是有点混乱,因为排他锁被第二个线程授予(UPREQ 清除并且 excl 计数递增)但第一个线程仍然负责设置 lk_lockholder 字段。