简体   繁体   中英

Implementing a Mutex Lock in C

I'm trying to make a really simple spinlock mutex in C and for some reason I'm getting cases where two threads are getting the lock at the same time, which shouldn't be possible. It's running on a multiprocessor system which may be why there's a problem. Any ideas why it's not working?

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
    }
    } 
}

Well for a start you're testing an uninitialised variable ( failure ) the first time the while() condition is executed.

Your actual problem is that you're telling gcc to use a register for mx_state - which clearly won't work for a spinlock. Try:

    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"
    );

Note that asm volatile is also important here, to ensure that it doesn't get hoisted out of your while loop.

The problem is that you load mx_state into a register (the 'r' constraint) and then do the exchange with the registers, only writing back the result into mx_state at the end of the asm code. What you want is something more like

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"
    );

Even this is somewhat dangerous, as in theory the compiler could load the mx_state, spill it into a local temp stack slot, and do the xchg there. It also is somewhat inefficient, as it has extra movs hardcoded that may not be needed but can't be eliminated by the optimizer. You're better off using a simpler asm that expands to a single instruction, such as

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

Note how we force the use of mx_state in place, by using it's address rather than its value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM