【发布时间】:2016-03-21 03:15:08
【问题描述】:
我的自旋锁实现如下所示,我认为它不会导致任何数据竞争,但是当我使用 -fsanitize=thread 测试我的代码时,它报告 spin_unlock 有写入数据竞争。这怎么可能发生?是误报吗?
#define barrier() asm volatile("": : :"memory")
#define cpu_relax() asm volatile("pause\n": : :"memory")
static inline void spin_lock(volatile int *lock) {
while (1)
{
int i = 0;
if (!atomic_swap(lock, EBUSY)) return;
while (*lock) {
i++;
if (i == 4000) {
i = 0;
thread_yield();
}
cpu_relax();
}
}
}
static inline void spin_unlock(volatile int *lock) {
barrier();
*lock = 0;
}
atomic_swap 是一个函数:
static inline int atomic_swap(volatile void *lockword, int value) {
unsigned long tmp;
int result;
__asm__ __volatile__ ("dmb" : : : "memory");
__asm__ __volatile__("@ atomic_swap\n"
"1: ldrex %0, [%2]\n"
" strex %1, %3, [%2]\n"
" teq %1, #0\n"
" bne 1b"
: "=&r" (result), "=&r" (tmp)
: "r" (lockword), "Ir" (value)
: "cc");
__asm__ __volatile__ ("dmb" : : : "memory");
return result;
}
【问题讨论】:
-
我对 ARM 一无所知,但我对竞态条件知之甚少。我怀疑您的问题是您没有检查 ldrex 中返回的值。在您的循环中检查
lock,但有可能在thread1 看到它变为0 和ldrex 返回值之间,thread2 可能突然介入并更改了它。所以2个线程都会写EBUSY。可能不是你想要的。 -
另外,您正在更改内存中的值(锁定字),但没有将其列为输出。考虑将
[%2]更改为%2,将其从输入移至输出,并将其约束更改为+m。 -
它在 intex x64 平台上,在 ARM 上没有。
-
我不相信“intex”是一个“平台”。 Intex 销售 [手机](www.intex.in/mobiles/smart-phones/)(即 aqua 智能手机)-> 他们的手机包含处理器(即 Snapdragon 615)。 Snapdragon 处理器是一个 ARM 平台。
ldrex、strex&dmb都是针对 ARM 平台的指令。但即使我错了,您似乎很清楚您正在从内存中加载一个值 (ldrex %0, [%2]),但没有检查该值是什么。您只是“希望”在执行while (*lock)和执行ldrex之间没有任何改变。这是一个竞争条件。
标签: c++ linux multithreading crash locking