C++11 multithreading: Valgrind uninitialized value(s) warning - c++

I have compiled the following dummy program under Linux using gcc 8.2.1:
#include <iostream>
#include <mutex>
#include <thread>
struct Foo
{
void start() {
thread = std::thread(&Foo::run, this);
}
void stop() {
mutex.lock();
done = true;
mutex.unlock();
thread.join();
}
void run() {
bool tmp;
for (;;) {
mutex.lock();
tmp = done;
mutex.unlock();
if (tmp)
break;
}
}
std::thread thread;
std::mutex mutex;
bool done;
};
int main()
{
Foo foo;
std::cout << "starting...\n";
foo.start();
std::cout << "stopping...\n";
foo.stop();
std::cout << "done\n";
}
If I subsequently run it under valgrind 3.14.0, I receive the following warning:
==30060== Thread 2:
==30060== Conditional jump or move depends on uninitialised value(s)
==30060== at 0x1095F3: Foo::run() (in /.../a.out)
==30060== by 0x109AAE: void std::__invoke_impl<void, void (Foo::*)(), Foo*>(std::__invoke_memfun_deref, void (Foo::*&&)(), Foo*&&) (in /.../a.out)
==30060== by 0x109771: std::__invoke_result<void (Foo::*)(), Foo*>::type std::__invoke<void (Foo::*)(), Foo*>(void (Foo::*&&)(), Foo*&&) (in /.../a.out)
==30060== by 0x10A012: decltype (__invoke((_S_declval<0ul>)(), (_S_declval<1ul>)())) std::thread::_Invoker<std::tuple<void (Foo::*)(), Foo*> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) (in /.../a.out)
==30060== by 0x109FB8: std::thread::_Invoker<std::tuple<void (Foo::*)(), Foo*> >::operator()() (in /.../a.out)
==30060== by 0x109F8D: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (Foo::*)(), Foo*> > >::_M_run() (in /.../a.out)
==30060== by 0x496A062: execute_native_thread_routine (thread.cc:80)
==30060== by 0x4894A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==30060== by 0x4CD7A42: clone (in /usr/lib/libc-2.28.so)
I am not completely sure what is causing this, I have written this snippet in hopes of diagnosing a bug in a more complicated class (that I cannot post here) I am currently working on and which produces exceptions when calling the equivalent of Foo::stop(). Does the valgrind warning imply some serious misunderstanding of the C++ threading interface on my part? And assuming for a moment that Foo::run would actually do something useful, how could I fix this program while keeping Foo's interface the way it is?

What is the initial value of bool done; ? It is indeterminate (some garbage value), so your thread (run) can be stopped without calling stop method.
done must be initialized:
//...
std::mutex mutex;
bool done = false; // <--

Related

TSAN thread race error detected with Boost intrusive_ptr use

Below code generates a TSAN error(race condition).
Is this a valid error ? or a false positive ?
Object is destroyed only after ref count becomes zero(after all other thread memory operations are visible - with atomic_thread_fence)
If I use a std::shared_ptr instead of boost::intrusive_ptr, then TSAN error disappears.
Since both threads use the object as read-only, I presume this should be safe.
If this is a valid error how do I fix it ?
gcc version - 7.3.1 boost version - 1.72.0
compile command : "g++ -ggdb -I /usr/local/boost_1_72_0 -O3 -fsanitize=thread TSan_Intr_Ptr.cpp -lpthread
"
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/detail/spinlock.hpp>
#include <boost/atomic.hpp>
#include <thread>
#include <iostream>
#include <vector>
#include <atomic>
#include <unistd.h>
using namespace std;
struct Shared
{
mutable boost::atomic<int> refcount_;
//From https://www.boost.org/doc/libs/1_72_0/doc/html/atomic/usage_examples.html
friend void intrusive_ptr_add_ref(const Shared * x)
{
x->refcount_.fetch_add(1, boost::memory_order_relaxed);
}
friend void intrusive_ptr_release(const Shared* x)
{
if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1)
{
boost::atomic_thread_fence(boost::memory_order_acquire);
delete x;
}
}
};
vector<boost::intrusive_ptr<Shared const>> g_vec;
boost::detail::spinlock g_lock = BOOST_DETAIL_SPINLOCK_INIT;
void consumer()
{
while(true)
{
g_lock.lock();
g_vec.clear();
g_lock.unlock();
usleep(10);
}
}
int main()
{
thread thd(consumer);
while(true)
{
boost::intrusive_ptr<Shared const> p(new Shared);
g_lock.lock();
g_vec.push_back(p);
g_lock.unlock();
usleep(1);
}
return 0;
}
TSAN Error
WARNING: ThreadSanitizer: data race (pid=14513)
Write of size 8 at 0x7b0400000010 by main thread:
#0 operator delete(void*) <null> (libtsan.so.0+0x00000006fae4)
#1 intrusive_ptr_release(Shared const*) /Test/TSan_Intr_Ptr_Min.cpp:25 (a.out+0x000000401195)
#2 boost::intrusive_ptr<Shared const>::~intrusive_ptr() /boost_1_72_0/boost/smart_ptr/intrusive_ptr.hpp:98 (a.out+0x000000401195)
#3 main /x01/exch/Test/TSan_Intr_Ptr_Min.cpp:51 (a.out+0x000000401195)
Previous atomic write of size 4 at 0x7b0400000010 by thread T1:
#0 __tsan_atomic32_fetch_sub <null> (libtsan.so.0+0x00000006576f)
#1 boost::atomics::detail::gcc_atomic_operations<4ul, true>::fetch_sub(unsigned int volatile&, unsigned int, boost::memory_order) /boost_1_72_0/boost/atomic/detail/ops_gcc_atomic.hpp:116 (a.out+0x000000401481)
#2 boost::atomics::detail::base_atomic<int, int>::fetch_sub(int, boost::memory_order) volatile /usr/local/boost_1_72_0/boost/atomic/detail/atomic_template.hpp:348 (a.out+0x000000401481)
#3 intrusive_ptr_release(Shared const*) /Test/TSan_Intr_Ptr_Min.cpp:22 (a.out+0x000000401481)
...
it seems using memory_order_acq_rel resolves the issue. (May be https://www.boost.org/doc/libs/1_72_0/doc/html/atomic/usage_examples.html example is in-correct)
friend void intrusive_ptr_add_ref(const Shared * x)
{
x->refcount_.fetch_add(1, boost::memory_order_acq_rel);
}
friend void intrusive_ptr_release(const Shared* x)
{
if (x->refcount_.fetch_sub(1, boost::memory_order_acq_rel) == 1)
{
delete x;
}
}

Symmetric transfer does not prevent stack-overflow for C++20 coroutines

According to the blog post C++ Coroutines: Understanding Symmetric Transfer symmetric transfer allows you to suspend one coroutine and resume another without consuming any additional stack space. This prevents stack-overflows, which can occur when coroutines contain loops and co_await tasks that can potentially complete synchronously within the body of that loop.
Even though the following code example uses symmetric transfer, it crashes due to a stack-overflow. Please note that the code below is a minimal example to reproduce the stack-overflow: e.g., if I include the definition of the destructor of type Type in the header file, then I don't get a stack-overflow.
// type.h
#pragma once
struct Type {
~Type();
};
// type.cc
#include "type.h"
Type::~Type() {}
// main.cc
#include <cstdint>
#include <exception>
#include <type_traits>
#include <utility>
#include "type.h"
#if __has_include(<coroutine>) // when using g++
#include <coroutine>
namespace coro {
using std::coroutine_handle;
using std::noop_coroutine;
using std::suspend_always;
} // namespace coro
#elif __has_include(<experimental/coroutine>) // when using clang++
#include <experimental/coroutine>
namespace coro {
using std::experimental::coroutine_handle;
using std::experimental::noop_coroutine;
using std::experimental::suspend_always;
} // namespace coro
#endif
template <typename T = void>
class Task {
public:
struct PromiseBase {
friend struct final_awaitable;
struct final_awaitable {
bool await_ready() const noexcept { return false; }
template <typename PROMISE>
coro::coroutine_handle<> await_suspend(
coro::coroutine_handle<PROMISE> coro) noexcept {
if (coro.promise().m_continuation) {
return coro.promise().m_continuation;
} else {
// The top-level task started from within main() does not have a
// continuation. This will give control back to the main function.
return coro::noop_coroutine();
}
}
void await_resume() noexcept {}
};
coro::suspend_always initial_suspend() noexcept { return {}; }
auto final_suspend() noexcept { return final_awaitable{}; }
void unhandled_exception() noexcept { std::terminate(); }
void set_continuation(coro::coroutine_handle<> continuation) noexcept {
m_continuation = continuation;
}
private:
coro::coroutine_handle<> m_continuation;
};
struct PromiseVoid : public PromiseBase {
auto get_return_object() { return coroutine_handle_t::from_promise(*this); }
void return_void() noexcept {}
void result() {}
};
struct PromiseT : public PromiseBase {
auto get_return_object() { return coroutine_handle_t::from_promise(*this); }
void return_value(T&& v) { value = std::move(v); }
T&& result() && { return std::move(value); }
T value;
};
using promise_type =
std::conditional_t<std::is_same_v<T, void>, PromiseVoid, PromiseT>;
using coroutine_handle_t = coro::coroutine_handle<promise_type>;
Task(coroutine_handle_t coroutine) : m_coroutine(coroutine) {}
~Task() {
if (m_coroutine) {
m_coroutine.destroy();
}
}
void start() noexcept { m_coroutine.resume(); }
auto operator co_await() const noexcept { return awaitable{m_coroutine}; }
private:
struct awaitable {
coroutine_handle_t m_coroutine;
awaitable(coroutine_handle_t coroutine) noexcept : m_coroutine(coroutine) {}
bool await_ready() const noexcept { return false; }
coro::coroutine_handle<> await_suspend(
coro::coroutine_handle<> awaitingCoroutine) noexcept {
m_coroutine.promise().set_continuation(awaitingCoroutine);
return m_coroutine;
}
auto await_resume() { return std::move(m_coroutine.promise()).result(); }
};
coroutine_handle_t m_coroutine;
};
Task<Type> coro2() { co_return Type{}; }
Task<> coro1() { auto s = co_await coro2(); }
Task<> test() {
for (std::uint64_t i = 0; i != 50000000; ++i) {
co_await coro1();
}
}
int main() {
auto task = test();
task.start();
}
I compile the code using clang++ version 12.0.1 and g++ version 11.1.0:
clang++-12 main.cc type.cc -std=c++20 -stdlib=libc++ -O3 -fsanitize=address
g++-11 main.cc type.cc -std=c++20 -O3 -fsanitize=address
Here is the truncated output for clang++:
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==20846==ERROR: AddressSanitizer: stack-overflow on address 0x7ffc76b1aff8 (pc 0x0000004cb7ab bp 0x7ffc76b1b050 sp 0x7ffc76b1afa0 T0)
#0 0x4cb7ab in coro1() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cb7ab)
#1 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#2 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#3 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#4 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#5 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#6 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#7 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#8 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#9 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#10 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#11 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#12 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#13 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#14 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#15 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#16 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#17 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#18 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#19 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#20 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#21 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#22 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#23 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#24 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
#25 0x4cbe4a in test() (.resume) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x4cbe4a)
...
Here is the truncated output for g++:
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==21434==ERROR: AddressSanitizer: stack-overflow on address 0x7fff2904dff8 (pc 0x7fd5f7825180 bp 0x7fff2904e880 sp 0x7fff2904dff0 T0)
#0 0x7fd5f7825180 in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned long, unsigned long, void*, bool, unsigned int) ../../../../src/libsanitizer/asan/asan_stack.cpp:57
#1 0x7fd5f781b0eb in __sanitizer::BufferedStackTrace::Unwind(unsigned long, unsigned long, void*, bool, unsigned int) ../../../../src/libsanitizer/sanitizer_common/sanitizer_stacktrace.h:122
#2 0x7fd5f781b0eb in operator delete(void*) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:160
#3 0x5643118400b7 in _Z5coro2v.destroy(coro2()::_Z5coro2v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x20b7)
#4 0x564311840e36 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2e36)
#5 0x56431183fe20 in _Z5coro2v.actor(coro2()::_Z5coro2v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x1e20)
#6 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#7 0x564311841741 in _Z4testv.actor(test()::_Z4testv.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x3741)
#8 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#9 0x56431183fe20 in _Z5coro2v.actor(coro2()::_Z5coro2v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x1e20)
#10 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#11 0x564311841741 in _Z4testv.actor(test()::_Z4testv.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x3741)
#12 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#13 0x56431183fe20 in _Z5coro2v.actor(coro2()::_Z5coro2v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x1e20)
#14 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#15 0x564311841741 in _Z4testv.actor(test()::_Z4testv.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x3741)
#16 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#17 0x56431183fe20 in _Z5coro2v.actor(coro2()::_Z5coro2v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x1e20)
#18 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#19 0x564311841741 in _Z4testv.actor(test()::_Z4testv.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x3741)
#20 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#21 0x56431183fe20 in _Z5coro2v.actor(coro2()::_Z5coro2v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x1e20)
#22 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#23 0x564311841741 in _Z4testv.actor(test()::_Z4testv.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x3741)
#24 0x564311840f15 in _Z5coro1v.actor(coro1()::_Z5coro1v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x2f15)
#25 0x56431183fe20 in _Z5coro2v.actor(coro2()::_Z5coro2v.frame*) (/home/leonard/Desktop/hiwi/async_io_uring/stack_overflow/a.out+0x1e20)
Interestingly, if I compile with clang++-12 main.cc type.cc -std=c++20 -stdlib=libc++ -O0 -fsanitize=address the program does not trigger a stack-overflow and exits without any errors. Furthermore, if I omit -fsanitize=address, then I get a segmentation fault when using -O3 and no error at all when using -O0.
Can anyone tell me what I am doing wrong?
I faced a similar issue when playing around with coroutines. I am not 100% certain of the reason why
the stack builds up but this is what I think might happen.
First of all, I don't think symmetric transfer is a given, it depends on compiler optimization and in some cases it might be difficult for the compiler to make this tail-call transformation. One of the reason could be because of the non-trivial destructor that lays in another compilation unit for Type (this is just a guess).
Reading the blog post you mentioned, it says: "The bool-returning version, however, can have a slight win in terms of optimisability in some cases compared to the symmetric-transfer form.", so it might be because compiler support is not fully mature yet (?) and it might be a good alternative to try the bool-returning form instead.
I would love to have a good answer on this problem as well, just trying to give my opinion based on my current finding at the moment, so please don't take this answer as absolute truth.
Edit:
Here is a workaround that prevents the stack-overflow. It uses the bool-returning version of the await_suspend() function. Unfortunately, the workaround introduces other problems. For example, the Task type is not thread-safe anymore. For further information look at the section "The Coroutines TS solution" of the blog post C++ Coroutines: Understanding Symmetric Transfer.
// in main.cc
struct PromiseBase {
// ...
struct final_awaitable {
// ...
template <typename PROMISE>
void await_suspend(coro::coroutine_handle<PROMISE> coro) noexcept {
if (coro.promise().m_continuation &&
std::exchange(coro.promise().ready, true)) {
// coro did not complete synchronously, therefore we need to resume
// the continuation
coro.promise().m_continuation.resume();
}
}
// ...
};
bool ready{false};
// ...
};
// in main.cc
struct awaitable {
// ...
// The bool returning version of await_suspend resumes awaitingCoroutine
// without consuming any additional stack-space if the value false is
// returned. Otherwise, it returns control to the caller/resumer of
// awaitingCoroutine.
bool await_suspend(coro::coroutine_handle<> awaitingCoroutine) noexcept {
m_coroutine.promise().set_continuation(awaitingCoroutine);
m_coroutine.resume();
// resume awaitingCoroutine if m_coroutine completed synchronously
return !std::exchange(m_coroutine.promise().ready, true);
}
// ...
};

Thread sanitizer reports data race when using std::packaged_task/std::exception_ptr

I am experiencing some issues with thread sanitizer (TSan) complaining about a data race in some production code where std::packaged_task are handed over to a dispatcher thread by wrapping them in a std::function. For this question I have simplified what it does in production, while triggering TSan. The implementation is similar to the answer given by Anthony Williams in this question (at least that is my understanding): Non-obvious lifetime issue with std::promise and std::future.
Note that the same data race is reported if using std::promise. It will also complain if a simple int is thrown/catched. No race is found if the exception is not accessed in the catch clause. No race is found if I omit using the wrapping lambda and simply std::move the std::packaged_task to the thread.
#include <future>
#include <thread>
#include <stdexcept>
#include <string>
void throw_exception_func()
{
throw std::runtime_error("test_exception");
}
int main()
{
for(int i = 0; i<1000; ++i) {
std::packaged_task<void()> packaged_task(throw_exception_func);
std::future<void> task_future = packaged_task.get_future();
const std::function<void()> wrapper_func = [&]{
std::packaged_task<void()> pt = std::packaged_task<void()>(std::move(packaged_task));
pt();
};
std::thread task_thread(wrapper_func);
bool expected_exception = false;
try {
task_future.get();
} catch (const std::runtime_error& e) {
expected_exception = std::string(e.what()).substr(0, 4) == "test"; // Do something with the exception.
}
task_thread.join();
if(!expected_exception) {
throw std::runtime_error("Did not get expected exception");
};
}
return 0;
}
TSan output when compiled with clang++-3.6 test_packaged_task.cpp -g3 -std=c++14 -pthread -fsanitize=thread -D_GLIBCXX_DEBUG:
SUMMARY: ThreadSanitizer: data race ??:0 operator delete(void*)
==================
==================
WARNING: ThreadSanitizer: data race (pid=70669)
Write of size 8 at 0x7d2400004d00 by thread T3:
#0 free <null> (a.out+0x000000463eab)
#1 std::__exception_ptr::exception_ptr::exception_ptr(std::__exception_ptr::exception_ptr const&) <null> (libstdc++.so.6+0x00000008d4c8)
#2 ~_Result /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:611 (a.out+0x0000004e08f7)
#3 std::__future_base::_Result<void>::_M_destroy() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:616 (a.out+0x0000004e084c)
#4 std::__future_base::_Result_base::_Deleter::operator()(std::__future_base::_Result_base*) const /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:198 (a.out+0x0000004d3f3a)
#5 ~unique_ptr /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/unique_ptr.h:236 (a.out+0x0000004d3cff)
#6 ~_State_baseV2 /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:313 (a.out+0x0000004d95c3)
#7 ~_Task_state_base /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:564 (a.out+0x0000004d941f)
#8 ~_Task_state /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:567 (a.out+0x0000004d921a)
#9 void __gnu_cxx::new_allocator<int>::destroy<std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()> >(std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>*) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/ext/new_allocator.h:124 (a.out+0x0000004d91b4)
#10 void std::allocator_traits<std::allocator<int> >::destroy<std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()> >(std::allocator<int>&, std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>*) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/alloc_traits.h:542 (a.out+0x0000004d9140)
#11 std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>, std::allocator<int>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/shared_ptr_base.h:531 (a.out+0x0000004d8d75)
#12 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/shared_ptr_base.h:150 (a.out+0x0000004ce7fd)
#13 ~__shared_count /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/shared_ptr_base.h:659 (a.out+0x0000004ce770)
#14 ~__shared_ptr /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/shared_ptr_base.h:925 (a.out+0x0000004d3de9)
#15 ~shared_ptr /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/shared_ptr.h:93 (a.out+0x0000004d3d93)
#16 ~packaged_task /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:1504 (a.out+0x0000004cbd90)
#17 operator() /home/chop/tardis_dev~chop_linux1606/tardis_dev/build_clang/test_packaged_task.cpp:20 (a.out+0x0000004cac4b)
#18 void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/mutex:738 (a.out+0x0000004dcdeb)
#19 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:386 (a.out+0x0000004dfee9)
#20 std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:1403 (a.out+0x0000004d9d54)
#21 std::packaged_task<void ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:1547 (a.out+0x0000004d2674)
#22 operator() /home/chop/tardis_dev~chop_linux1606/tardis_dev/build_clang/test_packaged_task.cpp:19 (a.out+0x0000004cac3d)
#23 std::_Function_handler<void (), main::$_0>::_M_invoke(std::_Any_data const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/functional:1871 (a.out+0x0000004ca748)
#24 std::function<void ()>::operator()() const /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/functional:2267 (a.out+0x0000004d14b0)
#25 void std::_Bind_simple<std::function<void ()> ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/functional:1530 (a.out+0x0000004d1370)
#26 std::_Bind_simple<std::function<void ()> ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/functional:1520 (a.out+0x0000004d1310)
#27 std::thread::_Impl<std::_Bind_simple<std::function<void ()> ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/thread:115 (a.out+0x0000004d12b9)
#28 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null> (libstdc++.so.6+0x0000000b8c6f)
Previous read of size 8 at 0x7d2400004d00 by main thread:
#0 main /home/chop/tardis_dev~chop_linux1606/tardis_dev/build_clang/test_packaged_task.cpp:27 (a.out+0x0000004c9e10)
#1 std::future<void>::get() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:846 (a.out+0x0000004cb6b5)
#2 main /home/chop/tardis_dev~chop_linux1606/tardis_dev/build_clang/test_packaged_task.cpp:25 (a.out+0x0000004c9c9c)
#3 std::future<void>::get() /usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/future:846 (a.out+0x0000004cb6b5)
#4 main /home/chop/tardis_dev~chop_linux1606/tardis_dev/build_clang/test_packaged_task.cpp:25 (a.out+0x0000004c9c9c)
... repeated....
Compiling with GCC 5.2.1 and GCC 5.3.1 g++ test_packaged_task.cpp -g3 -std=c++14 -pthread -fsanitize=thread -D_GLIBCXX_DEBUGalso gives races, but the TSan output is slightly different:
==================
WARNING: ThreadSanitizer: data race (pid=75845)
Write of size 8 at 0x7d2400009b30 by thread T5:
#0 free <null> (libtsan.so.0+0x000000025819)
#1 <null> <null> (libstdc++.so.6+0x00000008d4c8)
#2 std::__future_base::_Result<void>::~_Result() /usr/include/c++/5/future:611 (a.out+0x00000040e159)
#3 std::__future_base::_Result<void>::_M_destroy() /usr/include/c++/5/future:616 (a.out+0x0000004058f2)
#4 std::__future_base::_Result_base::_Deleter::operator()(std::__future_base::_Result_base*) const /usr/include/c++/5/future:198 (a.out+0x000000404d2d)
#5 std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::~unique_ptr() /usr/include/c++/5/bits/unique_ptr.h:236 (a.out+0x000000406042)
#6 std::__future_base::_State_baseV2::~_State_baseV2() /usr/include/c++/5/future:313 (a.out+0x00000040daa1)
#7 std::__future_base::_Task_state_base<void ()>::~_Task_state_base() /usr/include/c++/5/future:1360 (a.out+0x00000040db55)
#8 std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>::~_Task_state() /usr/include/c++/5/future:1387 (a.out+0x00000040dfb5)
#9 void __gnu_cxx::new_allocator<int>::destroy<std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()> >(std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>*) /usr/include/c++/5/ext/new_allocator.h:124 (a.out+0x00000040eced)
#10 void std::allocator_traits<std::allocator<int> >::destroy<std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()> >(std::allocator<int>&, std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>*) /usr/include/c++/5/bits/alloc_traits.h:542 (a.out+0x00000040e9dd)
#11 std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<void (*)(), std::allocator<int>, void ()>, std::allocator<int>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() /usr/include/c++/5/bits/shared_ptr_base.h:531 (a.out+0x00000040e464)
#12 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/5/bits/shared_ptr_base.h:150 (a.out+0x00000040848a)
#13 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/5/bits/shared_ptr_base.h:659 (a.out+0x000000405f1c)
#14 std::__shared_ptr<std::__future_base::_Task_state_base<void ()>, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/5/bits/shared_ptr_base.h:925 (a.out+0x0000004078cd)
#15 std::shared_ptr<std::__future_base::_Task_state_base<void ()> >::~shared_ptr() /usr/include/c++/5/bits/shared_ptr.h:93 (a.out+0x0000004078f9)
#16 std::packaged_task<void ()>::~packaged_task() /usr/include/c++/5/future:1504 (a.out+0x0000004079bb)
#17 main::{lambda()#1}::operator()() const <null> (a.out+0x000000403814)
#18 _M_invoke /usr/include/c++/5/functional:1871 (a.out+0x000000403cda)
#19 std::function<void ()>::operator()() const /usr/include/c++/5/functional:2267 (a.out+0x00000040f630)
#20 void std::_Bind_simple<std::function<void ()> ()>::_M_invoke<>(std::_Index_tuple<>) /usr/include/c++/5/functional:1531 (a.out+0x00000040f254)
#21 std::_Bind_simple<std::function<void ()> ()>::operator()() /usr/include/c++/5/functional:1520 (a.out+0x00000040ec1e)
#22 std::thread::_Impl<std::_Bind_simple<std::function<void ()> ()> >::_M_run() /usr/include/c++/5/thread:115 (a.out+0x00000040e8e8)
#23 <null> <null> (libstdc++.so.6+0x0000000b8c6f)
Previous read of size 8 at 0x7d2400009b30 by main thread:
#0 main /home/chop/tardis_dev~chop_linux1606/tardis_dev/build/test_packaged_task.cpp:27 (a.out+0x0000004039f8)
Thread T5 (tid=76123, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.0+0x000000027577)
#1 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>, void (*)()) <null> (libstdc++.so.6+0x0000000b8db2)
#2 main /home/chop/tardis_dev~chop_linux1606/tardis_dev/build/test_packaged_task.cpp:22 (a.out+0x0000004038f5)
SUMMARY: ThreadSanitizer: data race ??:0 __interceptor_free
==================
Can anyone help ? Is this a false positive by TSan ?

How can I avoid an exception thrown by <thread> in C++11?

I am trying to write multi-thread software using the thread library in C++11. Some basic tutorials found on the internet can compile and run as expected. But my own application separated into classes always throws an exception.
Could anyone kindly tell me which part of the code should be fixed, please?
$ clang++ -std=c++11 -stdlib=libc++ BaseInterface.cxx -c -o BaseInterface.o
$ clang++ -std=c++11 -stdlib=libc++ SocketReceiver.cxx -c -o SocketReceiver.o
$ clang++ -std=c++11 -stdlib=libc++ main.cxx -c -o main.o
$ clang++ -std=c++11 -stdlib=libc++ main.o BaseInterface.o SocketReceiver.o -o main
$ gdb main
(gdb) run
Starting program: /Users/oxon/test/main
Reading symbols for shared libraries ++............................ done
libc++abi.dylib: terminate called throwing an exception
Program received signal SIGABRT, Aborted.
[Switching to process 859 thread 0x40b]
0x00007fff88df8212 in __pthread_kill ()
(gdb) bt
#0 0x00007fff88df8212 in __pthread_kill ()
#1 0x00007fff8bc85af4 in pthread_kill ()
#2 0x00007fff8bcc9dce in abort ()
#3 0x00007fff894d3a17 in abort_message ()
#4 0x00007fff894d13c6 in default_terminate ()
#5 0x00007fff89874887 in _objc_terminate ()
#6 0x00007fff894d13f5 in safe_handler_caller ()
#7 0x00007fff894d1450 in std::terminate ()
#8 0x00007fff894d25b7 in __cxa_throw ()
#9 0x00007fff8a9ba3b9 in std::__1::thread::join ()
#10 0x0000000100000cf0 in SocketReceiver::Receive ()
#11 0x0000000100000c6d in SocketReceiver::DoReceive ()
#12 0x0000000100001593 in _ZNSt3__114__thread_proxyINS_5tupleIJPFPvS2_ES2_EEEEES2_S2_ ()
#13 0x00007fff8bc84742 in _pthread_start ()
#14 0x00007fff8bc71181 in thread_start ()
This is the result on OS X 10.8. Scientific Linux 6 with GCC 4.4 also gives a similar result.
= BaseInterface.h =
#ifndef BASE_INTERFACE_H
#define BASE_INTERFACE_H
#include "SocketReceiver.h"
class BaseInterface
{
private:
SocketReceiver* fReceiver;
public:
BaseInterface();
virtual ~BaseInterface();
virtual void Close();
virtual void Open();
;
#endif
= BaseInterface.cxx =
#include <iostream>
#include <string.h>
#include <unistd.h>
#include "BaseInterface.h"
BaseInterface::BaseInterface()
{
fReceiver = new SocketReceiver(this);
}
//______________________________________________________________________________
BaseInterface::~BaseInterface()
{
Close();
delete fReceiver;
fReceiver = 0;
}
//______________________________________________________________________________
void BaseInterface::Close()
{
fReceiver->Stop();
usleep(10000);
while(fReceiver->IsRunning()){
usleep(10000);
} // while
}
//______________________________________________________________________________
void BaseInterface::Open()
{
fReceiver->Start();
}
= SocketReceiver.h =
#ifndef SOCKET_RECEIVER_H
#define SOCKET_RECEIVER_H
#include <thread>
#include <mutex>
class BaseInterface;
class SocketReceiver
{
private:
BaseInterface* fInterface;
bool fIsRunning;
std::mutex fMutex;
bool fStop;
std::thread* fThread;
public:
SocketReceiver(BaseInterface* interface = 0);
virtual ~SocketReceiver();
bool IsRunning() const {return fThread ? true : false;}
static void* DoReceive(void* arg);
void Receive();
void Start();
void Stop();
};
#endif
= SocketReceiver.cxx =
#include <iostream>
#include <thread>
#include <unistd.h>
#include "BaseInterface.h"
#include "SocketReceiver.h"
SocketReceiver::SocketReceiver(BaseInterface* interface)
{
fInterface = interface;
fStop = true;
fThread = 0;
}
//______________________________________________________________________________
SocketReceiver::~SocketReceiver()
{
}
//______________________________________________________________________________
void* SocketReceiver::DoReceive(void* arg)
{
SocketReceiver* receiver = (SocketReceiver*)arg;
receiver->Receive();
return 0;
}
//______________________________________________________________________________
void SocketReceiver::Receive()
{
while(not fStop){
fMutex.lock();
usleep(10000);
fMutex.unlock();
} // while
fThread->join();
delete fThread;
fThread = 0;
}
//______________________________________________________________________________
void SocketReceiver::Start()
{
fStop = false;
fThread = new std::thread(DoReceive, (void*)this);
}
//______________________________________________________________________________
void SocketReceiver::Stop()
{
fStop = true;
}
= main.cxx =
#include "BaseInterface.h"
int main()
{
BaseInterface interface;
interface.Open();
interface.Close();
return 0;
}
You're joining the thread in itself. Call join in the parent thread, like in SocketReceiver::Stop.
When join is called, it cleans up underlying data. And if you call it in the thread you're running then that data will be removed while the thread still needs it.
If the function run by a std::thread exits via and exception then std::terminate will be called.
std::thread::join() is specified to throw std::system_error with an error code resource_deadlock_would_occur if join() is called in the same thread, i.e. a thread cannot join itself. So your attempt to make the thread join itself causes an exception which terminates the process. A thread cannot join itself, joining is defined as "wait for the thread to complete" so obviously cannot be done by the thread itself, it would block waiting for itself to finish, which can't happen until the call to join() finishes, which can't happen until the thread finishes, which can't happen until the call to join() finishes ... can you see where this is going?
Also why are you doing this?
fThread = new std::thread(DoReceive, (void*)this);
std::thread is not pthread_create, you don't need to pass it void*, just get rid of DoReceive and call:
fThread = new std::thread(&SocketReceiver::Receive, this);
And why is your std::thread allocated with new? That's not necessary either, you can use a std::thread member and use joinable() to check if it's active or not, instead of checking if the pointer is non-null.

Incorrect stack trace in core by uncaught std::exception

There is my code:
#include <string>
#include <tr1/functional>
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;
class Event
{
public:
typedef std::tr1::function<void()> Handler;
void set(Handler h)
{
m_handler = h;
}
template<typename T, typename F>
void set(T * obj, F memfn)
{
set(std::tr1::bind(memfn, obj));
}
void operator()()
{
m_handler();
}
static void fire(Event * event) throw ()
{
(*event)();
}
Handler m_handler;
};
class BuggyHandler
{
public:
BuggyHandler()
{
}
BuggyHandler(Event * b) :
bar(b)
{
bar->set(this, &BuggyHandler::HandleEvent);
}
void HandleEvent()
{
// throw std::length_error
std::string().append(std::numeric_limits<size_t>::max(), '0');
}
private:
Event * bar;
};
void get_correct_stacktrace()
{
Event bar;
BuggyHandler handler(&bar);
bar();
}
void get_incorrect_stacktrace()
{
Event bar;
BuggyHandler handler(&bar);
Event::fire(&bar);
}
int main(int argc, char **argv)
{
int opt = atoi(argv[1]);
if (opt)
get_correct_stacktrace();
else
get_incorrect_stacktrace();
}
When I call ./test 1, I can get correct stack trace from core:
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xf7d028d0 in raise () from /lib/libc.so.6
#2 0xf7d03ff3 in abort () from /lib/libc.so.6
#3 0xf7ede880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4 0xf7edc2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5 0xf7edc2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6 0xf7edc41a in __cxa_throw () from /usr/lib/libstdc++.so.6
#7 0xf7e73c6f in std::__throw_length_error () from /usr/lib/libstdc++.so.6
#8 0xf7eb9a17 in std::string::append () from /usr/lib/libstdc++.so.6
#9 0x08049b96 in BuggyHandler::HandleEvent (this=0xffc26c9c) at /home/liangxu/release/server_2.0/test/src/test.cc:54
#10 0x08049857 in get_correct_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:67
#11 0x080498e0 in main (argc=Cannot access memory at address 0x5ac6) at /home/liangxu/release/server_2.0/test/src/test.cc:81
The location of throw exception is test.cc:54
When I call ./test 0, I can get incorrect stack trace from core:
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xf7d508d0 in raise () from /lib/libc.so.6
#2 0xf7d51ff3 in abort () from /lib/libc.so.6
#3 0xf7f2c880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4 0xf7f2a2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5 0xf7f2a2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6 0xf7f2a305 in std::exception::what () from /usr/lib/libstdc++.so.6
#7 0xf7f29d98 in __cxa_call_unexpected () from /usr/lib/libstdc++.so.6
#8 0x080497eb in get_incorrect_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:30
#9 0x080498f5 in main (argc=Cannot access memory at address 0x5adf) at /home/liangxu/release/server_2.0/test/src/test.cc:83
There aren't the location of throw exception.
My compiler is "gcc (GCC) 4.1.2 20070115 (prerelease) (SUSE Linux)"
If compiled with "-fno-exceptions", both methods generated correct stack trace.
What is the reason?
Both stack traces are correct.
When you call Event::fire, the exception is thrown in HandleEvent and stack unwinding happens until it encounters the fire which has that exception specification.
If you don't know the real behaviour of exception specifications, you can read about it here : http://www.gotw.ca/publications/mill22.htm
Basically, the specification throw () does guarantee that this method does not throw by failing miserably if one of the containing calls throws. When stack unwinding tries to go out of this method, it checks the exception specification, sees that it doesn't match, and calls std::unexpected from the current location of unwiding, thus a __cxa_call_unexpected () in your stack trace just after get_incorrect_stacktrace ().
In most cases, using exception specifications is useless in C++ because the guarantee is provided at the cost of a general failure of the program is something throws and shouldn't.