C++ Valgrind:使用atomic::compare\u exchange\u时,条件跳转或移动取决于未初始化的值
我有一个任务来实现一个非常基本的无锁排序向量(只有插入和索引),我让一切都正常工作,但是valgrind说我有一个条件跳转/移动,这取决于一个未初始化的值。我正在使用--track origins=是的,但我觉得它并没有那么有用 这是我的索引运算符的代码:C++ Valgrind:使用atomic::compare\u exchange\u时,条件跳转或移动取决于未初始化的值,c++,multithreading,valgrind,lock-free,C++,Multithreading,Valgrind,Lock Free,我有一个任务来实现一个非常基本的无锁排序向量(只有插入和索引),我让一切都正常工作,但是valgrind说我有一个条件跳转/移动,这取决于一个未初始化的值。我正在使用--track origins=是的,但我觉得它并没有那么有用 这是我的索引运算符的代码: int operator[](int pos) { Pair pdata_old = pdata.load(); Pair pdata_new = pdata_old; // Increment ref count
int operator[](int pos) {
Pair pdata_old = pdata.load();
Pair pdata_new = pdata_old;
// Increment ref count
do {
pdata_new = pdata_old;
++pdata_new.ref_count;
} while (!pdata.compare_exchange_weak(pdata_old, pdata_new));
// Get old data
int ret_val = (*pdata_new.pointer)[pos];
pdata_old = pdata.load();
// Decrement ref count
do {
pdata_new = pdata_old;
--pdata_new.ref_count;
// assert(pdata_new.ref_count >= 0);
} while (!pdata.compare_exchange_weak(pdata_old, pdata_new));
return ret_val;
}
Pair只是一个包含向量*和int的结构,构造函数初始化它的所有值。我找不到任何依赖未初始化数据的地方,至少通过查看我的代码
下面是相关的valgrind输出(第121行是函数声明所在的行,第130和142行是比较交换弱()行):
==21299==
==21299==线程2:
==21299==条件跳转或移动取决于未初始化的值
==21299==at 0x10A5C2:LFSV::operator[](int)(LFSV.h:130)
==21299==by 0x1099F4:read_position_0()(driver.cpp:27)
==21299==by 0x10FCC6:void std::u invoke_impl(std::u invoke_other,void(*&&)()(invoke.h:60)
==21299==by 0x10FC5C:std::u invoke_result::type std::u invoke(void(*&&)()(invoke.h:95)
==21299==0x10FC34:_ZNSt6thread8_invokerist5tuplejpfveee9_invokeijlm0eedtcsr3stde8_invokespcl10_S_declvalix_eeest12_Index_tupleIJXspT_EEE(线程:234)
==21299==by 0x10FC04:std::thread::u Invoker::operator()(线程:243)
==21299==by 0x10FAE8:std::thread::_State\u impl::_M_run()(线程:186)
==21299==by 0x50FAB9E:execute\u native\u thread\u例程(thread.cc:83)
==21299==by 0x593208B:start_线程(在/usr/lib/libpthread-2.26.so中)
==21299==by0x5c3ee7e:clone(在/usr/lib/libc-2.26.so中)
==21299==未初始化的值是由堆栈分配创建的
==21299==at 0x10A520:LFSV::operator[](int)(LFSV.h:121)
==21299==
==21299==条件跳转或移动取决于未初始化的值
==21299==at 0x10A654:LFSV::operator[](int)(LFSV.h:142)
==21299==by 0x1099F4:read_position_0()(driver.cpp:27)
==21299==by 0x10FCC6:void std::u invoke_impl(std::u invoke_other,void(*&&)()(invoke.h:60)
==21299==by 0x10FC5C:std::u invoke_result::type std::u invoke(void(*&&)()(invoke.h:95)
==21299==0x10FC34:_ZNSt6thread8_invokerist5tuplejpfveee9_invokeijlm0eedtcsr3stde8_invokespcl10_S_declvalix_eeest12_Index_tupleIJXspT_EEE(线程:234)
==21299==by 0x10FC04:std::thread::u Invoker::operator()(线程:243)
==21299==by 0x10FAE8:std::thread::_State\u impl::_M_run()(线程:186)
==21299==by 0x50FAB9E:execute\u native\u thread\u例程(thread.cc:83)
==21299==by 0x593208B:start_线程(在/usr/lib/libpthread-2.26.so中)
==21299==by0x5c3ee7e:clone(在/usr/lib/libc-2.26.so中)
==21299==未初始化的值是由堆栈分配创建的
==21299==at 0x10A520:LFSV::operator[](int)(LFSV.h:121)
==21299==
在带有填充的对象上使用比较交换
时,这是正常的,不需要担心。它可能会导致虚假的CAS故障,因此,如果您只使用一次而没有重试循环或其他操作,请担心
Pair只是一个包含向量*和int的结构
并在正常的64位C++实现上填充,其中<代码> siZeof(vector *)=8</COD>和<代码> siZeof(int)=4 < /COD>,和<代码>对齐(vector *)=8</Cord> 要使每个指针成员8字节对齐,整个结构/类需要8字节对齐,因此其大小必须填充到8的倍数,以便
Pair foo[]
数组正常工作,每个数组元素具有8字节对齐
但是比较\u交换\u弱
比较整个对象的位模式,包括填充。
大概您是在没有优化的情况下编译的,编译器为int
成员创建了一个4字节存储,将局部存储到堆栈中的代码,然后为x86-64的cmpxchg16b
指令加载两个8字节,将其加载回整个对,或者(如果atomic
不是无锁的)获取锁并有效地执行memcmp
==21299==
==21299== Thread 2:
==21299== Conditional jump or move depends on uninitialised value(s)
==21299== at 0x10A5C2: LFSV::operator[](int) (lfsv.h:130)
==21299== by 0x1099F4: read_position_0() (driver.cpp:27)
==21299== by 0x10FCC6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:60)
==21299== by 0x10FC5C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:95)
==21299== by 0x10FC34: _ZNSt6thread8_InvokerISt5tupleIJPFvvEEEE9_M_invokeIJLm0EEEEDTclsr3stdE8__invokespcl10_S_declvalIXT_EEEEESt12_Index_tupleIJXspT_EEE (thread:234)
==21299== by 0x10FC04: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (thread:243)
==21299== by 0x10FAE8: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (thread:186)
==21299== by 0x50FAB9E: execute_native_thread_routine (thread.cc:83)
==21299== by 0x593208B: start_thread (in /usr/lib/libpthread-2.26.so)
==21299== by 0x5C3EE7E: clone (in /usr/lib/libc-2.26.so)
==21299== Uninitialised value was created by a stack allocation
==21299== at 0x10A520: LFSV::operator[](int) (lfsv.h:121)
==21299==
==21299== Conditional jump or move depends on uninitialised value(s)
==21299== at 0x10A654: LFSV::operator[](int) (lfsv.h:142)
==21299== by 0x1099F4: read_position_0() (driver.cpp:27)
==21299== by 0x10FCC6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:60)
==21299== by 0x10FC5C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:95)
==21299== by 0x10FC34: _ZNSt6thread8_InvokerISt5tupleIJPFvvEEEE9_M_invokeIJLm0EEEEDTclsr3stdE8__invokespcl10_S_declvalIXT_EEEEESt12_Index_tupleIJXspT_EEE (thread:234)
==21299== by 0x10FC04: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (thread:243)
==21299== by 0x10FAE8: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (thread:186)
==21299== by 0x50FAB9E: execute_native_thread_routine (thread.cc:83)
==21299== by 0x593208B: start_thread (in /usr/lib/libpthread-2.26.so)
==21299== by 0x5C3EE7E: clone (in /usr/lib/libc-2.26.so)
==21299== Uninitialised value was created by a stack allocation
==21299== at 0x10A520: LFSV::operator[](int) (lfsv.h:121)
==21299==