В последнее время я читал код 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, то есть он получил исключительность!