Valgrind:条件跳转或移动取决于使用 atomic::compare_exchange_weak 时未初始化的值
Valgrind: Conditional jump or move depends on uninitialised value(s) when using atomic::compare_exchange_weak
我有一个任务要实现一个非常基本的无锁排序向量(只有插入和索引),并且我一切正常,但是,valgrind 说我有一个条件 jump/move 取决于在一个未初始化的值上。我正在使用 --track-origins=yes,但我发现它并没有那么有用。
这是我的索引运算符的代码:
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 只是一个包含 vector* 和 int 的结构,构造函数初始化它的所有值。我找不到任何依赖未初始化数据的地方,至少通过查看我的代码是这样。
这是相关的 valgrind 输出(第 121 行是声明函数的行,130 和 142 是 compare_exchange_weak() 行):
==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==
这是正常现象,在对带有填充 的对象使用 compare_exchange_weak
时无需担心。它可能导致虚假的 CAS 失败,所以如果您只使用 compare_exchange_strong
一次而没有重试循环或其他东西,请担心。
Pair is just a struct that contains a vector* and an int
因此在普通的 64 位 C++ 实现上有填充,其中 sizeof(vector*) == 8
和 sizeof(int) == 4
,以及 alignof(vector*) == 8
.
为了使每个指针成员8字节对齐,整个struct/class需要8对齐,因此它的大小必须填充到8的倍数,这样[=的数组15=] 工作正常,每个数组元素有 8 字节对齐。
但是compare_exchange_weak
比较的是整个对象的bit-pattern,包括padding。
大概你编译时没有优化,编译器生成的代码将局部变量存储到堆栈,为 int
成员使用 4 字节存储,然后将其加载回整个 Pair
x86-64 的 cmpxchg16b
指令的两个 8 字节加载,或者(如果 atomic<Pair>
不是 lock-free)获取锁并有效地执行 memcmp
.
我有一个任务要实现一个非常基本的无锁排序向量(只有插入和索引),并且我一切正常,但是,valgrind 说我有一个条件 jump/move 取决于在一个未初始化的值上。我正在使用 --track-origins=yes,但我发现它并没有那么有用。
这是我的索引运算符的代码:
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 只是一个包含 vector* 和 int 的结构,构造函数初始化它的所有值。我找不到任何依赖未初始化数据的地方,至少通过查看我的代码是这样。
这是相关的 valgrind 输出(第 121 行是声明函数的行,130 和 142 是 compare_exchange_weak() 行):
==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==
这是正常现象,在对带有填充 的对象使用 compare_exchange_weak
时无需担心。它可能导致虚假的 CAS 失败,所以如果您只使用 compare_exchange_strong
一次而没有重试循环或其他东西,请担心。
Pair is just a struct that contains a vector* and an int
因此在普通的 64 位 C++ 实现上有填充,其中 sizeof(vector*) == 8
和 sizeof(int) == 4
,以及 alignof(vector*) == 8
.
为了使每个指针成员8字节对齐,整个struct/class需要8对齐,因此它的大小必须填充到8的倍数,这样[=的数组15=] 工作正常,每个数组元素有 8 字节对齐。
但是compare_exchange_weak
比较的是整个对象的bit-pattern,包括padding。
大概你编译时没有优化,编译器生成的代码将局部变量存储到堆栈,为 int
成员使用 4 字节存储,然后将其加载回整个 Pair
x86-64 的 cmpxchg16b
指令的两个 8 字节加载,或者(如果 atomic<Pair>
不是 lock-free)获取锁并有效地执行 memcmp
.