您的位置:首页 > 编程语言 > Go语言

Chandler Carruth's terrified bug in GoingNative 2013

2013-12-01 16:13 393 查看
The code.

1 #include <thread>

2 #include <mutex>

3 #include <condition_variable>

4 #include <iostream>

5

6 using namespace std;

7

8 class A {

9 public:

10 virtual void F() = 0;

11 void Done() {

12 F();

13 lock_guard<mutex> l{m};

14 is_done = true;

15 cv.notify_one();

16 cout << "Called Done...\n";

17 }

18

19 virtual ~A() {

20 unique_lock<mutex> l{m};

21 cout << "Waiting for Done...\n";

22 cv.wait(l, [&] {return is_done;});

23 cout << "Destroying object...\n";

24 }

25

26 private:

27 mutex m;

28 condition_variable cv;

29 bool is_done{false};

30 };

31

32 class B: public A {

33 public:

34 virtual void F() {

35 }

36

37 ~B() {

38 cout << "~B done\n";

39 }

40 };

41

42 int main() {

43 A *obj{new B};

44

45 thread t1{[=] {

46 obj->F();

47 obj->Done();

48 }};

49

50 thread t2{[=] {delete obj;}};

51 t1.join();

52 t2.join();

53

54 return 0;

55 }

56

The bug.

Thread 1 (t1):

call B::F() # need access vptr because F() is a overridden virtual function.

call A::Done()

Thread 2 (t2):

call "delete obj"

call B::~B() # need access "vptr", because A::~A() is virtual, ~B() is virtual by default,.

# For now, we are still good, no problem (all read access to vptr)

call A::~A() # need change the "vptr" of class A to "vptr" of class B because in the destructor

# of A, it only can call virtual function of A, not the derived class. Remember virtual

# function doesn't work in ctor/dtor. This write to "vptr" will cause data race

# condition with line 46 in thread 1, which needs access the "vptr" there

clang diagnosis message

ghost@ubuntu:~/work/test$ c34 thread_bug.cxx -fsanitize=thread

ghost@ubuntu:~/work/test$ ./a.out

Called Done...

~B done

==================

WARNING: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call) (pid=21270)

Write of size 8 at 0x7d1c0000df90 by thread T2:

#0 A::~A() /home/ghost/work/test/thread_bug.cxx:19 (a.out+0x0000000b061e)

#1 B::~B() /home/ghost/work/test/thread_bug.cxx:39 (a.out+0x0000000b055e)

#2 B::~B() /home/ghost/work/test/thread_bug.cxx:37 (a.out+0x0000000b04b7)

#3 main::$_1::operator()() const /home/ghost/work/test/thread_bug.cxx:50 (a.out+0x0000000abd60)

#4 void std::_Bind_simple<main::$_1 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/gcc48/include/c++/4.8.2/functional:1731 (a.out+0x0000000abc70)

#5 std::_Bind_simple<main::$_1 ()>::operator()() /usr/gcc48/include/c++/4.8.2/functional:1720 (a.out+0x0000000abc10)

#6 std::thread::_Impl<std::_Bind_simple<main::$_1 ()> >::_M_run() /usr/gcc48/include/c++/4.8.2/thread:115 (a.out+0x0000000abbb9)

#7 execute_native_thread_routine /home/ghost/work/gcc/gcc-4.8.2/x86_64-unknown-linux-gnu/libstdc++-v3/src/c++11/../../../.././libstdc++-v3/src/c++11/thread.cc:84 (libstdc++.so.6+0x0000000b045f)

Previous read of size 8 at 0x7d1c0000df90 by thread T1:

#0 main::$_0::operator()() const /home/ghost/work/test/thread_bug.cxx:46 (a.out+0x0000000ae83d)

#1 void std::_Bind_simple<main::$_0 ()>::_M_invoke<>(std::_Index_tuple<>) /usr/gcc48/include/c++/4.8.2/functional:1731 (a.out+0x0000000ae770)

#2 std::_Bind_simple<main::$_0 ()>::operator()() /usr/gcc48/include/c++/4.8.2/functional:1720 (a.out+0x0000000ae710)

#3 std::thread::_Impl<std::_Bind_simple<main::$_0 ()> >::_M_run() /usr/gcc48/include/c++/4.8.2/thread:115 (a.out+0x0000000ae6b9)

#4 execute_native_thread_routine /home/ghost/work/gcc/gcc-4.8.2/x86_64-unknown-linux-gnu/libstdc++-v3/src/c++11/../../../.././libstdc++-v3/src/c++11/thread.cc:84 (libstdc++.so.6+0x0000000b045f)

Location is heap block of size 104 at 0x7d1c0000df90 allocated by main thread:

#0 operator new(unsigned long) /home/ghost/work/llvm/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:559 (a.out+0x00000004b2b9)

#1 main /home/ghost/work/test/thread_bug.cxx:43 (a.out+0x0000000a955f)

Thread T2 (tid=21274, running) created by main thread at:

#0 pthread_create /home/ghost/work/llvm/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:876 (a.out+0x00000004ef6b)

#1 __gthread_create /home/ghost/work/gcc/gcc-4.8.2/x86_64-unknown-linux-gnu/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:662 (libstdc++.so.6+0x0000000b06ae)

#2 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) /home/ghost/work/gcc/gcc-4.8.2/x86_64-unknown-linux-gnu/libstdc++-v3/src/c++11/../../../.././libstdc++-v3/src/c++11/thread.cc:142 (libstdc++.so.6+0x0000000b06ae)

#3 main /home/ghost/work/test/thread_bug.cxx:50 (a.out+0x0000000a95ec)

Thread T1 (tid=21273, finished) created by main thread at:

#0 pthread_create /home/ghost/work/llvm/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc:876 (a.out+0x00000004ef6b)

#1 __gthread_create /home/ghost/work/gcc/gcc-4.8.2/x86_64-unknown-linux-gnu/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:662 (libstdc++.so.6+0x0000000b06ae)

#2 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) /home/ghost/work/gcc/gcc-4.8.2/x86_64-unknown-linux-gnu/libstdc++-v3/src/c++11/../../../.././libstdc++-v3/src/c++11/thread.cc:142 (libstdc++.so.6+0x0000000b06ae)

#3 main /home/ghost/work/test/thread_bug.cxx:45 (a.out+0x0000000a95b6)

SUMMARY: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call) /home/ghost/work/test/thread_bug.cxx:19 A::~A()

==================

Waiting for Done...

Destroying object...

ThreadSanitizer: reported 1 warnings

The way out

factor out the synchronization code out of dctor into a separate method and before delete the object, wait for that method to finish
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: