1

我正在尝试使用原子汇编指令“bts”在 C 中实现互斥锁,以原子方式设置一个位并返回原始值。但是,当我运行以下代码时,它偶尔会出现死锁并经常显示竞态条件:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

typedef unsigned char mutex;
#define MUTEX_FREE 0
#define MUTEX_BUSY 1

// adapted from http://www.acm.uiuc.edu/sigops/roll_your_own/i386/atomic.html
mutex testAndSet(mutex *m) {
    int result;
    asm ("bts $0, %1; sbbl %0, %0"
         :"=r" (result)
         :"m" (*m)
         :"memory");
    return (result & 1);
}

void P(mutex *m) {
    // Must use atomic testAndSet to avoid race conditions
    while(testAndSet(m) == MUTEX_BUSY)
        usleep(10);
}

void V(mutex *m) {
    *m = MUTEX_FREE;
}

//////////////
// Test:
//////////////

const int NTHREADS = 100;
const int NINCS = 100;

int counter = 0;
mutex m = MUTEX_FREE;

void criticalSection() {
    int i;
    for(i=0;i<NINCS;i++) {
        P(&m);
        counter++;
        V(&m);
    }
}

int main() {
    int i;
    pthread_t threads[NTHREADS];
    for(i=0; i<NTHREADS; i++) {
        pthread_create(&threads[i], NULL, (void *) &criticalSection, NULL);
    }
    for(i=0; i<NTHREADS; i++) {
        pthread_join(threads[i], NULL);
    }
    printf("got counter=%d, expected=%d\n", counter, NTHREADS*NINCS);
}

如果我使用“xchgb”指令而不是“bts”,代码似乎可以工作,如下所示:

mutex testAndSet(mutex *m) {
    unsigned char result = MUTEX_BUSY;
    asm ("xchgb %1, %0"
            :"=m" (*m), "=r" (result)
            :"1" (result)
            :"memory");
    return result;
}

原始代码中的竞争条件在哪里?“bts”指令不应该是原子的,保证线程安全吗?

此外,我修改后的解决方案实际上是否正确?

(我正在运行 OS X 10.8 并使用 gcc 进行编译。)

4

1 回答 1

4

尝试使用LOCK前缀锁定内存总线:

asm ("lock bts $0, %1; ...");

该指令有效,因为无论前缀是否存在xchg,它总是断言信号。LOCK#LOCK

于 2012-11-07T20:13:45.483 回答