我想我已经发现了Helgrind工具返回的相当广泛的误报。也许这已在其他地方记录,但似乎Helgrind工具总是错误地将Test and Test-And-Set pattern误检测为误报。
struct resource {
int in_use;
int value;
pthread_mutex_t lock;
}
// assume each member of resource is initialized in the main function
// in_use is initialized to zero
// value is initialized to zero
// and the lock is initialized with pthread_mutex_init()
struct resource[1000];
void insertIntoUnused(int toInsert) {
int i;
for (i = 0; i < 1000; i++) {
if (resource[i].in_use == 0) {
pthread_mutex_lock(&resource[i].lock);
if (resource[i].in_use == 0) {
resource[i].in_use = 1;
resource[i].value = toInsert;
pthread_mutex_unlock(&resource[i].lock);
return;
}
pthread_mutex_unlock(&resource[i].lock);
}
}
}
如果许多线程运行上面的insertIntoUnused
函数,Helgrind将在第一次读取in_use
变量时检测到可能的竞争条件。但是,获取锁定后会再次检查in_use
变量,并且in_use
变量只有在获取锁定时才会写入。
TTAS模式是一种减少锁争用的超常用方法。我很惊讶Helgrind无法支持&#34;这种模式。
我理解为什么Helgrind算法在这里检测竞争条件。每次读取happens-before
变量与每次写入之间都没有in_use
关系。然而,要证明上述代码是正确的并不难(我实际上没有把它输出来......所以,抛开错别字,在文献中已经很好地建立了。)
我如何让Helgrind停止考虑首次检查潜在的竞争条件,以便在我的计划中找到其他竞争条件?
答案 0 :(得分:1)
POSIX线程模型表示对resource.in_use
的非同步访问会导致未定义的行为,无论在这种情况下它“似乎没问题”。
理论上,编译器可以利用它 - 例如,一旦循环的执行将resource.in_use
视为非零,就不需要再次测试它,因为(没有数据竞争) resource.in_use != 0
案例中没有任何内容可能导致该值发生变化。即。它可以将其改为:
for (i = 0; i < 1000; i++) {
if (resource.in_use != 0) {
i = 1000;
break;
}
pthread_mutex_lock(&resource[i].lock);
if (resource.in_use == 0) {
resource.in_use = 1;
resource.value = toInsert;
pthread_mutex_unlock(&resource[i].lock);
return;
}
pthread_mutex_unlock(&resource[i].lock);
}