Инструкция AMD mwaitx
позволяет дождаться изменения адреса и имеет ограниченную продолжительность. Невозможно определить, проснулся ли он из-за изменения значения или из-за прерывания.
Вы всегда можете проверить адрес, чтобы увидеть, не изменился ли он, но это приводит к проблеме ABA, когда он мог измениться, а затем измениться обратно.
Это может привести к проблеме, когда вы можете захотеть отправить запрос на обновление для блока данных, если был получен доступ к связанной с ним блокировке, но если блокировка получена, использована, а затем освобождена, структура данных изменилась, но значение блокировки не изменилось. t КАЖЕТСЯ изменился, и поток, использующий mwaitx
, не знает о доступе к блокировке.
Есть ли обходной путь для этого или я застрял?
@harold я добавил контекст
Стандартное решение проблемы ABA — прикрепить к значению счетчик изменений. Таким образом, если значение изменится обратно, счетчик изменений все равно будет другим.
@RaymondChen, что противоречит цели использования mwaitx, не так ли?
Нет. Если вы хотите использовать его для мониторинга блокировки, вы можете выбрать среднее значение младшего бита по сравнению со свободным, а старшие биты будут счетчиком, который делает его уникальным. Взять блокировку будет lock bts dword [mem], 0
для проверки и установки младшего бита. Снятие блокировки будет add dword [mem], 1
(без префикса lock
, так как никакие другие потоки не будут изменять значение; в лучшем случае они установят младший бит, который уже был установлен. Или просто загрузите значение блокировки, включите его и выполните обычный mov
выпуск-хранилище, но память-назначение add
эквивалентна.)
@PeterCordes В моем примере кода я считаю, что совершил ошибку: я думаю, что вы должны monitor
, прежде чем пытаться получить спин-блокировку, так как в противном случае возможно короткое окно, в котором другой поток мог снять блокировку до того, как вы установите монитор . Знаете ли вы рекомендуемую последовательность инструкций?
@fuz: О, хорошая мысль. IDK, если вы можете xchg
после monitor
, не вызывая мгновенного пробуждения. Возможно, если вы не получите блокировку с первой попытки, monitor
/ load / test+jcc / mwait
, чтобы у вас была окончательная проверка только для чтения после того, как монитор установлен, чтобы убедиться, что текущее значение соответствует тому, которое гарантирует спящий режим. Но это всего лишь предположение с моей стороны, я не перепроверял документы и не искал заведомо хороший пример, например, использует ли Linux mwait
в futex
, если на этом ядре нет другой задачи для запуска.
Идея состоит в том, чтобы использовать mwaitx
в качестве оптимизации, а не в качестве единственного примитива синхронизации. Для этого варианта использования не имеет значения, может ли он время от времени выходить из строя.
Скажем, например, вы хотите установить спин-блокировку
mutex dd 0
установив его на 1
, если раньше он удерживался 0
. Простой способ сделать это - дождаться, пока блокировка станет нулевой, например. так:
again: mov ebx, 1
xchg [mutex], ebx ; try to claim mutex
test ebx, ebx ; did we succeed?
jnz again ; if not, try again
Этот цикл, конечно, довольно неэффективен: если блокировка удерживается другим потоком, он очень быстро вращается, что приводит к большому количеству дорогостоящих обращений к шине RMW. Мы можем уменьшить нагрузку с помощью инструкции pause
, но было бы еще лучше, если бы мы узнали, что блокировка удерживается, и попытались бы потребовать ее только после того, как узнали, что другой поток освободил блокировку.
Инструкции monitor
/ mwait
предоставляют возможность сделать это: вы устанавливаете адрес для отслеживания, а затем вас замечают, когда происходит что-то интересное. Только тогда вы пытаетесь получить замок, спасая вас от вращения, если вы знаете, что не получите его. Не повредит, если mwait
вернется раньше: вы просто не получите замок, а затем вернетесь к его ожиданию.
again: mov ebx, 1
xchg [mutex], ebx ; try to claim mutex
test ebx, ebx ; did we succeed?
jz gotit
lea rax, [mutex] ; address to monitor
xor ecx, ecx ; no extensions
xor edx, edx ; no hints
monitor ; start to monitor the mutex
cmp [mutex], ebx ; did the mutex change state meanwhile? (see note)
jne again ; if yes, try to claim it again
xor ecx, ecx ; no extensions
xor eax, eax ; no hints
mwait ; wait for mutex to change
jmp again ; once it changed, try to get the lock again
gotit: ...
Примечание: проверка на изменение статуса необходима после настройки монитора, чтобы поймать другой поток, освобождающий мьютекс после того, как мы попытаемся его заявить, но до того, как мы активируем монитор.
Хотя это хорошо и все такое, есть новая проблема: если другой поток какое-то время не уступает, сложные реализации мьютекса могут захотеть переключиться на другую реализацию, например. тот, где ядро заботится о блокировке. Это позволяет ждать, пока блокировка станет доступной, без фактического запуска потока, освобождая ресурсы для других пользователей.
Этого трудно достичь с monitor
и mwait
: период ожидания неограничен и может быть очень долгим. Инструкции AMD mwaitx
и monitorx
очень похожи, но решают эту проблему: они позволяют вам установить тайм-аут, после которого mwaitx
возвращается, даже если область памяти не изменилась. Таким образом, вы можете использовать такой алгоритм, как «попытаться запросить блокировку 10 раз, вращая и ожидая, а затем перейти к блокировке на основе ядра» и быть достаточно уверенным во времени, которое требуется для выполнения.
Чтобы избежать состояния гонки, когда другой поток разблокируется между вашим monitor
и вашим mwait
, Энди Глю предлагает monitor
перед циклом, а затем внутри цикла проверить только для чтения и mwait
. blog.andy.glew.ca/2010/11/httpsemipublic.html Так монитор уже поставлен на охрану до последней проверки только для чтения перед сном. Видимо не нужно повторно запускать monitor
? Но в пользовательском пространстве вам, вероятно, следует перезапустить monitorx
, так как поток может быть на другом ядре ЦП, когда вы просыпаетесь, если он был отменен по прерыванию таймера.
@PeterCordes Я предполагаю, что переключение контекста в любом случае сделает мониторы недействительными. Статья показалась мне немного запутанной, поэтому я добавил код, который должен работать в любом случае.
Не уверен, каков ваш вариант использования, но обычно либо 1) он не меняется обратно, потому что это будет делать ваш код, либо 2) если кто-то еще изменил его, тогда все в порядке, вернитесь к ожиданию