Достигает ли пара futex wait / wake семантики получения / выпуска? - PullRequest
2 голосов
/ 17 мая 2019

Учитывая этот псевдокод, где глобально атомный int a инициализирован в 0:

Тема 1:

// ... some code here (X) ...
a.store(1, relaxed);
futex_wake(&a);

Тема 2:

if (futex_wait(&a, 1) == woken_up) {
  assert(a.load(relaxed) == 1);
  // ... some code here (Y) ...
}

Игнорирование возможности ложных пробуждений; можем ли мы вывести из приведенного выше кода, что код X синхронизируется с Y? По сути, это сводится к тому, предназначен ли сам futex для достижения семантики получения / выпуска через ожидание, которое пробуждается. .


Немного контекста: TSAN не понимает системный вызов futex (например, см. здесь , здесь ).

Теперь, обычно, при использовании futex для реализации мьютекса, семафора или какого-либо другого примитива синхронизации, у одного также есть атомарная переменная, которая загружается с порядком получения со стороны «блокировки», и хранится в порядке релиза стороной «разблокировки». (Выше я намеренно использую расслабленную семантику.)

Этого приобретения / освобождения достаточно для достижения синхронизации, формально правильно, и он распознается TSAN (который ничего не сообщает о блокировках, реализованных таким образом, например, QBasicMutex в Qt).

Этот вопрос в основном касается предложения, предложенного в сообщении на форуме , связанного выше , для обозначения операций futex самих с семантикой получения / выпуска. Будет ли такая пометка правильной?

(Я знаю, что абстрактная машина C ++ ничего не знает о futex. Она даже ничего не знает о pthreads, но TSAN знает, и знает, что, например, код, который происходит до pthread_create, также происходит до кода работает в новой ветке создания. Другими словами, это не вопрос языкового адвоката ...)

1 Ответ

1 голос
/ 17 мая 2019

С man futex(2):

Загрузка значения слова фьютекса, сравнение этого значения с ожидаемым значением и фактическая блокировка произойдетатомарно и будет полностью упорядочен по отношению к параллельным операциям, выполняемым другими потоками над одним и тем же словом futex. Таким образом, слово futex используется для соединения синхронизации в пространстве пользователя с реализацией блокировки ядром.По аналогии с атомарной операцией сравнения и обмена, которая потенциально изменяет разделяемую память, блокировка с помощью futex является атомарной операцией сравнения и блокировки.

Тот полный порядок соответствует C ++ std::memory_order_seq_cst:

Операция загрузки с этим порядком памяти выполняет операцию получения, хранилище выполняет операцию освобождения, а чтение-изменение-запись выполняет и операцию получения, и операцию освобождения, плюс существует единый общий порядок, в котором все потоки наблюдают все модификации в одном и том же порядке .

Другими словами, futex syscall делает в ядре эквивалент C ++11:

a.compare_exchange_strong(..., std::memory_order_seq_cst, std::memory_order_seq_cst);
...