DragonflyBSD: возможное состояние гонки в коде менеджера блокировок (kern_lock. c)? - PullRequest
0 голосов
/ 29 января 2020

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

1 Ответ

1 голос
/ 29 января 2020

Способ работает так: если один поток устанавливает UPREQ и затем спит, другой поток предоставит ему эксклюзивную блокировку и разбудит его. Поток предоставления очищает UPREQ и увеличивает эксклюзивный счетчик, но не знает, «кто» установил UPREQ, поэтому он до потока, который установил UPREQ, чтобы установить поле lockholder.

Поскольку код lockmgr должен иметь дело и с случаями голодания с множеством исключительных к единому доступу, с множеством обращений к единственному исключающему голоданием, и с вариантами взаимоблокировки, это довольно сложный процесс. Крайние случаи были обнаружены за последний год или два, но этот конкретный случай не выглядит для меня ошибкой. Просто немного сбивает с толку, поскольку исключительная блокировка предоставляется (UPREQ очищается и счетчик excl увеличивается) вторым потоком, но первый поток по-прежнему отвечает за установку поля lk_lockholder.

...