我实现了一个不可变的哈希图和附带的STM容器,它受clojure的atom
的启发,这类似于C ++的std::unique_ptr
,因为它通过指针管理(但不一定拥有)另一个对象,可以传递和换出,并且只能减少托管对象的引用计数,而不能完全破坏它。当运行一些用-O3
编译的测试代码时,我开始注意到错误的结果。
用于比较和交换的代码看起来像这样:
hashmap *update(stm *stm, update_callback cb, void *user_args)
{
while (1) {
lock(stm->mutex);
hashmap *current = stm->reference;
increment_ref_count(current); // Line 6
unlock(stm->mutex);
hashmap *aspirant = cb(current, user_args); // returns a new, thread local instance
increment_ref_count(aspirant);
// Position 1
lock(stm->mutex);
if (current == stm->reference) { // success, no modification while we were busy
stm->reference = aspirant;
increment_ref_count(aspirant); // stm now has a reference
unlock(stm->mutex);
// Position 2.1
decrement_ref_count(current); // release reference acquired at line 6
decrement_ref_count(current); // stm no longer has a reference
return aspirant;
} else { // reference was modified, loop and try again
unlock(stm->mutex);
// Position 2.2
decrement_ref_count(current); // release reference acquired at line 6
decrement_ref_count(aspirant); // ref_count now at zero, aspirant is free'd
}
}
}
increment_
和decrement_ref_count
自动增加/减少哈希图的引用计数。如果由于递减导致计数下降到零,则hasmap将在不久后释放。
设计用于引用计数指针的STM容器的挑战主要是关于获取引用并递增计数器原子,这就是为什么我在这里使用锁的原因。
作为测试,我正在使用hashmap + STM来统计单词列表中的出现次数。
如果我运行此处发布的代码,则不会发生竞争情况。 现在出现我的问题:如果将decrement_ref_count(current); // for line 6
从if/else
中移出,请远离Positions 2.1/2.2
(在第二个锁定区域之后),然后将其放在Position 1
(在第二个锁定区域之前) ),突然单词计数开始不正确,我也不知道为什么。
[我的论点是:a)在第二个关键区域中我不使用current
,并且b)因此,在比较和交换之前还是之后释放引用都没有关系。] >
显然,我有解决该问题的方法/解决方案;只需离开decrement
的位置即可,但我真的很想知道为什么会这样。
在“ Linux的Windows子系统”上编译为:gcc -Wall -Wcast-align -Wswitch-enum -Wswitch-default -Winit-self -pedantic -O3 -DNDEBUG -std=gnu11
。
我实现了一个不可变的哈希图和附带的STM容器,它受clojure原子的启发,也就是说,类似于C ++的std :: unique_ptr,因为它可以管理(但不一定是...