Я пытаюсь сделать действительно простое взаимное исключение спин-блокировки в C, и по некоторым причинам я получаю случаи, где два потока получают блокировку одновременно, которая не должна быть возможной. Это работает на многопроцессорной системе, которая может быть, почему существует проблема. Какие-либо идеи, почему это не работает?
void mutexLock(mutex_t *mutexlock, pid_t owner)
{
int failure = 1;
while(mutexlock->mx_state == 0 || failure || mutexlock->mx_owner != owner)
{
failure = 1;
if (mutexlock->mx_state == 0)
{
asm(
"movl $0x01,%%eax\n\t" // move 1 to eax
"xchg %%eax,%0\n\t" // try to set the lock bit
"mov %%eax,%1\n\t" // export our result to a test var
:"=r"(mutexlock->mx_state),"=r"(failure)
:"r"(mutexlock->mx_state)
:"%eax"
);
}
if (failure == 0)
{
mutexlock->mx_owner = owner; //test to see if we got the lock bit
}
}
}
Для начала вы тестируете неинициализированную переменную ( сбой
) при первом выполнении условия while ()
.
На самом деле ваша проблема в том, что вы говорите gcc использовать регистр для mx_state
- что явно не работает для спин-блокировки. Попробуйте:
asm volatile (
"movl $0x01,%%eax\n\t" // move 1 to eax
"xchg %%eax,%0\n\t" // try to set the lock bit
"mov %%eax,%1\n\t" // export our result to a test var
:"=m"(mutexlock->mx_state),"=r"(failure)
:"m"(mutexlock->mx_state)
:"%eax"
);
Обратите внимание, что asm volatile
также важен здесь, чтобы гарантировать, что он не будет выведен из вашего цикла while.
Проблема в том, что вы загружаете mx_state в регистр (ограничение 'r'), а затем выполняете обмен с регистрами, только записывая результат обратно в mx_state в конце asm-кода. Вам нужно нечто большее, чем
asm(
"movl $0x01,%%eax\n\t" // move 1 to eax
"xchg %%eax,%1\n\t" // try to set the lock bit
"mov %%eax,%0\n\t" // export our result to a test var
:"=r"(failure)
:"m" (mutexlock->mx_state)
:"%eax"
);
. Даже это несколько опасно, поскольку теоретически компилятор может загрузить mx_state, передать его в локальный слот временного стека и выполнить там xchg. Это также несколько неэффективно, так как в нем жестко запрограммированы дополнительные movs, которые могут не понадобиться, но не могут быть устранены оптимизатором. Вам лучше использовать более простой asm, который расширяется до одной инструкции, например
failure = 1;
asm("xchg %0,0(%1)" : "=r" (failure) : "r" (&mutex->mx_state), "0" (failure));
. Обратите внимание, как мы принудительно используем mx_state на месте, используя его адрес, а не его значение.