Skip to content

Instantly share code, notes, and snippets.

@inetic
Last active August 29, 2015 14:12
Show Gist options
  • Save inetic/b73a8a0f4186c8fe034d to your computer and use it in GitHub Desktop.
Save inetic/b73a8a0f4186c8fe034d to your computer and use it in GitHub Desktop.
#include <iostream>
#include <thread>
#include <vector>
using std::vector;
using std::string;
using std::make_shared;
// Compile using:
// # clang++ -stdlib=libc++ -std=c++11 -O0 -g -fsanitize=thread -lpthread -o test main.cpp
//
// Or:
// # clang++ -std=c++11 -O0 -g -fsanitize=thread -lpthread -o test main.cpp
void test3() {
std::unique_ptr<std::thread> thread;
{
auto output = make_shared<string>();
std::string str = "test";
thread.reset(new std::thread([str, output]() { *output += str; }));
// The str string now goes out of scope but due to COW
// the captured string may not have the copy of the content yet.
}
thread->join();
}
int main() {
vector<std::thread> threads;
// To increase the chance of the test failing we run it many times.
for (unsigned int i = 0; i < 100; ++i) {
threads.emplace_back([]() { test3(); });
}
for (auto& t : threads) {
t.join();
}
}
This warning is seen when libc++ is used.
WARNING: ThreadSanitizer: data race (pid=30829)
Write of size 8 at 0x7d0c0000bef8 by thread T62:
#0 operator delete(void*) <null>:0 (test+0x0000000485bb)
#1 std::__1::allocator<std::__1::__shared_ptr_emplace<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >::deallocate(std::__1::__shared_ptr_emplace<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >*, unsigned long) /usr/bin/../include/c++/v1/memory:1632 (test+0x0000000b29a7)
#2 ~shared_ptr /usr/bin/../include/c++/v1/memory:4448 (test+0x0000000ab36f)
#3 test3() /home/peter/project/thread-join-bug/main.cpp:19 (test+0x0000000a4241)
#4 operator() /home/peter/project/thread-join-bug/main.cpp:30 (test+0x0000000a909c)
#5 main::$_1&& std::__1::forward<main::$_1>(std::__1::remove_reference<main::$_1>::type&) /usr/bin/../include/c++/v1/type_traits:1549 (test+0x0000000a8968)
Previous write of size 1 at 0x7d0c0000befd by thread T5:
#0 std::__1::char_traits<char>::assign(char&, char const&) /usr/bin/../include/c++/v1/string:639 (test+0x0000000ae6d8)
#1 std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::append(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) /usr/bin/../include/c++/v1/string:2618 (test+0x0000000aa49e)
#2 test3()::$_0&& std::__1::forward<test3()::$_0>(std::__1::remove_reference<test3()::$_0>::type&) /usr/bin/../include/c++/v1/type_traits:1549 (test+0x0000000a957a)
Location is heap block of size 48 at 0x7d0c0000bee0 allocated by thread T62:
#0 operator new(unsigned long) <null>:0 (test+0x00000004804d)
#1 std::__1::allocator<std::__1::__shared_ptr_emplace<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >::allocate(unsigned long, void const*) /usr/bin/../include/c++/v1/memory:1630 (test+0x0000000b1192)
#2 __libcpp_compressed_pair_imp /usr/bin/../include/c++/v1/memory:2134 (test+0x0000000a3ba3)
#3 operator() /home/peter/project/thread-join-bug/main.cpp:30 (test+0x0000000a909c)
#4 main::$_1&& std::__1::forward<main::$_1>(std::__1::remove_reference<main::$_1>::type&) /usr/bin/../include/c++/v1/type_traits:1549 (test+0x0000000a8968)
Thread T62 (tid=30914, running) created by main thread at:
#0 pthread_create <null>:0 (test+0x00000004b501)
#1 thread<<lambda at main.cpp:30:26>, void> /usr/bin/../include/c++/v1/thread:354 (test+0x0000000a7c16)
#2 main::$_1&& std::__1::forward<main::$_1>(std::__1::remove_reference<main::$_1>::type&) /usr/bin/../include/c++/v1/type_traits:1549 (test+0x0000000a62a0)
#3 __libc_start_main /build/buildd/eglibc-2.19/csu/libc-start.c:287 (libc.so.6+0x000000021ec4)
Thread T5 (tid=30917, finished) created by thread T62 at:
#0 pthread_create <null>:0 (test+0x00000004b501)
#1 thread<<lambda at main.cpp:16:34>, void> /usr/bin/../include/c++/v1/thread:354 (test+0x0000000a5187)
#2 test3() /home/peter/project/thread-join-bug/main.cpp:16 (test+0x0000000a3f20)
#3 operator() /home/peter/project/thread-join-bug/main.cpp:30 (test+0x0000000a909c)
#4 main::$_1&& std::__1::forward<main::$_1>(std::__1::remove_reference<main::$_1>::type&) /usr/bin/../include/c++/v1/type_traits:1549 (test+0x0000000a8968)
This warning is seen when libstdc++ is used and when the line
"auto output = make_shared<string>();" is moved above the the opening bracket
of the local scope:
void test3() {
std::unique_ptr<std::thread> thread;
auto output = make_shared<string>();
{
std::string str = "test";
thread.reset(new std::thread([str, output]() { *output += str; }));
// The str string now goes out of scope but due to COW
// the captured string may not have the copy of the content yet.
}
thread->join();
}
WARNING: ThreadSanitizer: data race (pid=18519)
Write of size 1 at 0x7d080001efd8 by thread T34:
#0 operator delete(void*) <null>:0 (test+0x00000004bdbb)
#1 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() <null>:0 (libstdc++.so.6+0x0000000ba4de)
#2 operator() /home/peter/project/thread-join-bug/main.cpp:35 (test+0x0000000aacec)
#3 void std::_Bind_simple<main::$_1 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000aac40)
#4 std::_Bind_simple<main::$_1 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000aabe0)
#5 std::thread::_Impl<std::_Bind_simple<main::$_1 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000aab89)
#6 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
Previous read of size 1 at 0x7d080001efd8 by thread T58:
#0 memcpy <null>:0 (test+0x00000004c6d0)
#1 std::string::append(std::string const&) <null>:0 (libstdc++.so.6+0x0000000bb0d3)
#2 void std::_Bind_simple<test3()::$_0 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000ad8e0)
#3 std::_Bind_simple<test3()::$_0 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000ad880)
#4 std::thread::_Impl<std::_Bind_simple<test3()::$_0 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000ad829)
#5 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
Location is heap block of size 29 at 0x7d080001efc0 allocated by thread T34:
#0 operator new(unsigned long) <null>:0 (test+0x00000004b84d)
#1 std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) <null>:0 (libstdc++.so.6+0x0000000ba3b8)
#2 operator() /home/peter/project/thread-join-bug/main.cpp:35 (test+0x0000000aacec)
#3 void std::_Bind_simple<main::$_1 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000aac40)
#4 std::_Bind_simple<main::$_1 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000aabe0)
#5 std::thread::_Impl<std::_Bind_simple<main::$_1 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000aab89)
#6 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
Thread T34 (tid=18623, running) created by main thread at:
#0 pthread_create <null>:0 (test+0x00000004ed01)
#1 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) <null>:0 (libstdc++.so.6+0x0000000b1e3e)
#2 void __gnu_cxx::new_allocator<std::thread>::construct<std::thread, main::$_1>(std::thread*, main::$_1&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ext/new_allocator.h:120 (test+0x0000000a8340)
#3 _ZNSt16allocator_traitsISaISt6threadEE12_S_constructIS0_JZ4mainE3$_1EEENSt9enable_ifIXsr18__construct_helperIT_DpT0_EE5valueEvE4typeERS1_PS6_DpOS7_ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/alloc_traits.h:254 (test+0x0000000a8278)
#4 _ZNSt16allocator_traitsISaISt6threadEE9constructIS0_JZ4mainE3$_1EEEDTcl12_S_constructfp_fp0_spclsr3stdE7forwardIT0_Efp1_EEERS1_PT_DpOS5_ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/alloc_traits.h:393 (test+0x0000000a7b48)
#5 void std::vector<std::thread, std::allocator<std::thread> >::emplace_back<main::$_1>(main::$_1&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/vector.tcc:96 (test+0x0000000a7a31)
#6 main /home/peter/project/thread-join-bug/main.cpp:35 (test+0x0000000a7782)
Thread T58 (tid=18641, finished) created by thread T34 at:
#0 pthread_create <null>:0 (test+0x00000004ed01)
#1 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) <null>:0 (libstdc++.so.6+0x0000000b1e3e)
#2 test3() /home/peter/project/thread-join-bug/main.cpp:22 (test+0x0000000a7275)
#3 operator() /home/peter/project/thread-join-bug/main.cpp:35 (test+0x0000000aacec)
#4 void std::_Bind_simple<main::$_1 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000aac40)
#5 std::_Bind_simple<main::$_1 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000aabe0)
#6 std::thread::_Impl<std::_Bind_simple<main::$_1 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000aab89)
#7 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
This warning is seen when libstdc++ is used and when the output string is
created in the local scope as in the original cpp file above:
void test3() {
std::unique_ptr<std::thread> thread;
{
auto output = make_shared<string>();
std::string str = "test";
thread.reset(new std::thread([str, output]() { *output += str; }));
// The str string now goes out of scope but due to COW
// the captured string may not have the copy of the content yet.
}
thread->join();
}
WARNING: ThreadSanitizer: data race (pid=12796)
Write of size 8 at 0x7d080000cfc0 by thread T16:
#0 operator delete(void*) <null>:0 (test+0x00000004bdbb)
#1 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() <null>:0 (libstdc++.so.6+0x0000000ba4de)
#2 _ZNSt16allocator_traitsISaISsEE10_S_destroyISsEENSt9enable_ifIXsr16__destroy_helperIT_EE5valueEvE4typeERS0_PS4_ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/alloc_traits.h:281 (test+0x0000000b3410)
#3 void std::allocator_traits<std::allocator<std::string> >::destroy<std::string>(std::allocator<std::string>&, std::string*) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/alloc_traits.h:405 (test+0x0000000b3390)
#4 std::_Sp_counted_ptr_inplace<std::string, std::allocator<std::string>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/shared_ptr_base.h:407 (test+0x0000000b2fd1)
#5 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/shared_ptr_base.h:144 (test+0x0000000b06ad)
#6 ~__shared_count /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/shared_ptr_base.h:546 (test+0x0000000b0620)
#7 ~__shared_ptr /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/shared_ptr_base.h:781 (test+0x0000000b37b9)
#8 ~shared_ptr /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/shared_ptr.h:93 (test+0x0000000ae2d3)
#9 test3() /home/peter/project/thread-join-bug/main.cpp:24 (test+0x0000000a72b3)
#10 operator() /home/peter/project/thread-join-bug/main.cpp:34 (test+0x0000000aacac)
#11 void std::_Bind_simple<main::$_1 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000aac00)
#12 std::_Bind_simple<main::$_1 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000aaba0)
#13 std::thread::_Impl<std::_Bind_simple<main::$_1 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000aab49)
#14 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
Previous write of size 8 at 0x7d080000cfc0 by thread T21:
#0 operator new(unsigned long) <null>:0 (test+0x00000004b84d)
#1 std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) <null>:0 (libstdc++.so.6+0x0000000ba3b8)
#2 void std::_Bind_simple<test3()::$_0 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000ad8a0)
#3 std::_Bind_simple<test3()::$_0 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000ad840)
#4 std::thread::_Impl<std::_Bind_simple<test3()::$_0 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000ad7e9)
#5 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
Location is heap block of size 29 at 0x7d080000cfc0 allocated by thread T21:
#0 operator new(unsigned long) <null>:0 (test+0x00000004b84d)
#1 std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) <null>:0 (libstdc++.so.6+0x0000000ba3b8)
#2 void std::_Bind_simple<test3()::$_0 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000ad8a0)
#3 std::_Bind_simple<test3()::$_0 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000ad840)
#4 std::thread::_Impl<std::_Bind_simple<test3()::$_0 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000ad7e9)
#5 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
Thread T16 (tid=12869, running) created by main thread at:
#0 pthread_create <null>:0 (test+0x00000004ed01)
#1 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) <null>:0 (libstdc++.so.6+0x0000000b1e3e)
#2 void __gnu_cxx::new_allocator<std::thread>::construct<std::thread, main::$_1>(std::thread*, main::$_1&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ext/new_allocator.h:120 (test+0x0000000a8300)
#3 _ZNSt16allocator_traitsISaISt6threadEE12_S_constructIS0_JZ4mainE3$_1EEENSt9enable_ifIXsr18__construct_helperIT_DpT0_EE5valueEvE4typeERS1_PS6_DpOS7_ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/alloc_traits.h:254 (test+0x0000000a8238)
#4 _ZNSt16allocator_traitsISaISt6threadEE9constructIS0_JZ4mainE3$_1EEEDTcl12_S_constructfp_fp0_spclsr3stdE7forwardIT0_Efp1_EEERS1_PT_DpOS5_ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/alloc_traits.h:393 (test+0x0000000a7b08)
#5 void std::vector<std::thread, std::allocator<std::thread> >::_M_emplace_back_aux<main::$_1>(main::$_1&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/vector.tcc:408 (test+0x0000000a7caf)
#6 void std::vector<std::thread, std::allocator<std::thread> >::emplace_back<main::$_1>(main::$_1&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/vector.tcc:101 (test+0x0000000a7a48)
#7 main /home/peter/project/thread-join-bug/main.cpp:34 (test+0x0000000a7742)
Thread T21 (tid=12876, finished) created by thread T16 at:
#0 pthread_create <null>:0 (test+0x00000004ed01)
#1 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) <null>:0 (libstdc++.so.6+0x0000000b1e3e)
#2 test3() /home/peter/project/thread-join-bug/main.cpp:21 (test+0x0000000a7275)
#3 operator() /home/peter/project/thread-join-bug/main.cpp:34 (test+0x0000000aacac)
#4 void std::_Bind_simple<main::$_1 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1731 (test+0x0000000aac00)
#5 std::_Bind_simple<main::$_1 ()>::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1720 (test+0x0000000aaba0)
#6 std::thread::_Impl<std::_Bind_simple<main::$_1 ()> >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/thread:115 (test+0x0000000aab49)
#7 std::this_thread::__sleep_for(std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >) <null>:0 (libstdc++.so.6+0x0000000b1bef)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment