Chandler Carruth's terrified bug in GoingNative 2013

来源:互联网 发布:腾讯企业邮箱更换域名 编辑:程序博客网 时间:2024/06/06 12:28
 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):

  1. call B::F()  # need access vptr because F() is a overridden virtual function.
  2. call A::Done()
Thread 2 (t2):
  1. 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
原创粉丝点击