Ответ 1
У вас есть правильная идея, но ваш asm сломан:
cmpxchg
не может работать с непосредственным операндом, регистрируется только.
lock
не является допустимым префиксом для mov
. mov
к выровненному адресу является атомарным на x86, поэтому вам не нужно lock
в любом случае.
Прошло некоторое время с тех пор, как я использовал синтаксис AT & T, надеюсь, что я все вспомнил:
spin_lock:
xorl %ecx, %ecx
incl %ecx
spin_lock_retry:
xorl %eax, %eax
lock; cmpxchgl %ecx, (lock_addr)
jnz spin_lock_retry
ret
spin_unlock:
movl $0 (lock_addr)
ret
Обратите внимание, что GCC имеет атомные встроенные функции, поэтому вам не нужно использовать встроенный asm для выполнения этого:
void spin_lock(int *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1));
}
void spin_unlock(int volatile *p)
{
asm volatile (""); // acts as a memory barrier.
*p = 0;
}
Как сказано ниже в Боге, заблокированные инструкции несут затраты: каждый, который вы используете, должен очищать ваш кеш и блокировать вашу шину системной памяти, что может быть довольно дорого, если у вас достаточно процессоров. Даже без использования многих процессоров все еще легко и стоит оптимизировать:
void spin_lock(int volatile *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1))
{
while(*p) _mm_pause();
}
}
Инструкция pause
имеет жизненно важное значение для производительности на процессорах HyperThreading, когда у вас есть код, который вращается таким образом - он позволяет второму потоку выполняться во время вращения первого потока. На CPU, которые не поддерживают pause
, он рассматривается как nop
.