Реализация Mutex Lock в C - PullRequest
       12

Реализация Mutex Lock в C

7 голосов
/ 21 апреля 2010

Я пытаюсь создать действительно простой мьютекс спин-блокировки в 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
    }
    } 
}

Ответы [ 2 ]

7 голосов
/ 21 апреля 2010

Хорошо, для начала вы тестируете неинициализированную переменную (failure) при первом выполнении условия 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.

3 голосов
/ 21 апреля 2010

Проблема в том, что вы загружаете 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. Это также несколько неэффективно, поскольку в нем есть дополнительные жесткие коды, которые могут не понадобиться, но не могут быть устранены оптимизатором. Вам лучше использовать более простой asm, который расширяется до одной инструкции, такой как

failure = 1;
asm("xchg  %0,0(%1)" : "=r" (failure) : "r" (&mutex->mx_state), "0" (failure));

Обратите внимание, как мы принудительно используем mx_state, используя его адрес, а не его значение.

...