Proper locking for class that calls its own member functions from multiple threads. (Do I need to lock around accessing the "this" pointer?)

Viewed 87

I am working on writing a multithreaded application and it currently has a class that spawns threads and then calls its own member functions from within those threads. I thought this would be fine as long as the member function itself doesn't use any shared resources, but when I ran valgrind with the --tool=helgrind option to detect data races and other thread related issues it complains.

foo_main.cpp:

#include <iostream>
#include <mutex>
#include <thread>

class Foo final
{
public:
    void DoSomething() const
    {
        std::thread t1([&] {
            // Do I need to lock before accessing "this"?
            // std::lock_guard<std::mutex> lock(m_mutex);
            this->Print();
        });

        std::thread t2([&] {
            // Do I need to lock before accessing "this"?
            // std::lock_guard<std::mutex> lock(m_mutex);
            this->Print();
        });

        t1.join();
        t2.join();
    }
private:
    void Print() const
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        std::cout << "TEST" << std::endl;
    }

    mutable std::mutex m_mutex;
};

int main()
{
    Foo foo;
    foo.DoSomething();
}

Here is the output from valgrind:

valgrind --tool=helgrind ./foo
==27751== Helgrind, a thread error detector
==27751== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==27751== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==27751== Command: ./foo
==27751==
TEST
==27751== ---Thread-Announcement------------------------------------------
==27751==
==27751== Thread #1 is the program's root thread
==27751==
==27751== ---Thread-Announcement------------------------------------------
==27751==
==27751== Thread #2 was created
==27751==    at 0x7848ACE: clone (in /usr/lib64/libc-2.17.so)
==27751==    by 0x5051059: do_clone.constprop.4 (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x5052569: pthread_create@@GLIBC_2.2.5 (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x4C355BE: pthread_create_WRK (hg_intercepts.c:445)
==27751==    by 0x4C36A9B: pthread_create@* (hg_intercepts.c:478)
==27751==    by 0x531B596: std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) (in /usr/lib64/libstdc++.so.6.0.19)
==27751==    by 0x401C94: thread<Foo::DoSomething() const::__lambda0> (thread:135)
==27751==    by 0x401C94: Foo::DoSomething() const (foo_main.cpp:14)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==
==27751== ----------------------------------------------------------------
==27751==
==27751== Possible data race during write of size 8 at 0xA6A6E18 by thread #1
==27751== Locks held: none
==27751==    at 0x4018FA: ~_Impl_base (thread:97)
==27751==    by 0x4018FA: std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#1} ()> >::~_Impl() (thread:107)
==27751==    by 0x401508: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > > (new_allocator.h:124)
==27751==    by 0x401508: _S_destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > > (alloc_traits.h:281)
==27751==    by 0x401508: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > > (alloc_traits.h:405)
==27751==    by 0x401508: std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#1} ()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#1} ()> > >, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:407)
==27751==    by 0x401CD0: _M_release (shared_ptr_base.h:144)
==27751==    by 0x401CD0: ~__shared_count (shared_ptr_base.h:546)
==27751==    by 0x401CD0: ~__shared_ptr (shared_ptr_base.h:781)
==27751==    by 0x401CD0: ~shared_ptr (shared_ptr.h:93)
==27751==    by 0x401CD0: thread<Foo::DoSomething() const::__lambda0> (thread:135)
==27751==    by 0x401CD0: Foo::DoSomething() const (foo_main.cpp:14)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==
==27751== This conflicts with a previous read of size 8 by thread #2
==27751== Locks held: none
==27751==    at 0x531B316: ??? (in /usr/lib64/libstdc++.so.6.0.19)
==27751==    by 0x4C357B2: mythread_wrapper (hg_intercepts.c:406)
==27751==    by 0x5051EA4: start_thread (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x7848B0C: clone (in /usr/lib64/libc-2.17.so)
==27751==  Address 0xa6a6e18 is 24 bytes inside a block of size 56 alloc'd
==27751==    at 0x4C2D7B8: operator new(unsigned long) (vg_replace_malloc.c:422)
==27751==    by 0x401BEF: allocate (new_allocator.h:104)
==27751==    by 0x401BEF: allocate (alloc_traits.h:351)
==27751==    by 0x401BEF: __shared_count<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr_base.h:499)
==27751==    by 0x401BEF: __shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr_base.h:957)
==27751==    by 0x401BEF: shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr.h:316)
==27751==    by 0x401BEF: allocate_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr.h:598)
==27751==    by 0x401BEF: make_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr.h:614)
==27751==    by 0x401BEF: _M_make_routine<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (thread:193)
==27751==    by 0x401BEF: thread<Foo::DoSomething() const::__lambda0> (thread:135)
==27751==    by 0x401BEF: Foo::DoSomething() const (foo_main.cpp:14)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==  Block was alloc'd by thread #1
==27751==
==27751== ----------------------------------------------------------------
==27751==
==27751== Possible data race during read of size 8 at 0xA6A6E28 by thread #1
==27751== Locks held: none
==27751==    at 0x4018FD: ~__shared_count (shared_ptr_base.h:545)
==27751==    by 0x4018FD: ~__shared_ptr (shared_ptr_base.h:781)
==27751==    by 0x4018FD: ~shared_ptr (shared_ptr.h:93)
==27751==    by 0x4018FD: ~_Impl_base (thread:97)
==27751==    by 0x4018FD: std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#1} ()> >::~_Impl() (thread:107)
==27751==    by 0x401508: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > > (new_allocator.h:124)
==27751==    by 0x401508: _S_destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > > (alloc_traits.h:281)
==27751==    by 0x401508: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > > (alloc_traits.h:405)
==27751==    by 0x401508: std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#1} ()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#1} ()> > >, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:407)
==27751==    by 0x401CD0: _M_release (shared_ptr_base.h:144)
==27751==    by 0x401CD0: ~__shared_count (shared_ptr_base.h:546)
==27751==    by 0x401CD0: ~__shared_ptr (shared_ptr_base.h:781)
==27751==    by 0x401CD0: ~shared_ptr (shared_ptr.h:93)
==27751==    by 0x401CD0: thread<Foo::DoSomething() const::__lambda0> (thread:135)
==27751==    by 0x401CD0: Foo::DoSomething() const (foo_main.cpp:14)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==
==27751== This conflicts with a previous write of size 8 by thread #2
==27751== Locks held: none
==27751==    at 0x531B325: ??? (in /usr/lib64/libstdc++.so.6.0.19)
==27751==    by 0x4C357B2: mythread_wrapper (hg_intercepts.c:406)
==27751==    by 0x5051EA4: start_thread (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x7848B0C: clone (in /usr/lib64/libc-2.17.so)
==27751==  Address 0xa6a6e28 is 40 bytes inside a block of size 56 alloc'd
==27751==    at 0x4C2D7B8: operator new(unsigned long) (vg_replace_malloc.c:422)
==27751==    by 0x401BEF: allocate (new_allocator.h:104)
==27751==    by 0x401BEF: allocate (alloc_traits.h:351)
==27751==    by 0x401BEF: __shared_count<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr_base.h:499)
==27751==    by 0x401BEF: __shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr_base.h:957)
==27751==    by 0x401BEF: shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr.h:316)
==27751==    by 0x401BEF: allocate_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr.h:598)
==27751==    by 0x401BEF: make_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> >, std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (shared_ptr.h:614)
==27751==    by 0x401BEF: _M_make_routine<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > (thread:193)
==27751==    by 0x401BEF: thread<Foo::DoSomething() const::__lambda0> (thread:135)
==27751==    by 0x401BEF: Foo::DoSomething() const (foo_main.cpp:14)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==  Block was alloc'd by thread #1
==27751==
TEST
==27751== ---Thread-Announcement------------------------------------------
==27751==
==27751== Thread #3 was created
==27751==    at 0x7848ACE: clone (in /usr/lib64/libc-2.17.so)
==27751==    by 0x5051059: do_clone.constprop.4 (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x5052569: pthread_create@@GLIBC_2.2.5 (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x4C355BE: pthread_create_WRK (hg_intercepts.c:445)
==27751==    by 0x4C36A9B: pthread_create@* (hg_intercepts.c:478)
==27751==    by 0x531B596: std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) (in /usr/lib64/libstdc++.so.6.0.19)
==27751==    by 0x401E96: thread<Foo::DoSomething() const::__lambda1> (thread:135)
==27751==    by 0x401E96: Foo::DoSomething() const (foo_main.cpp:20)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==
==27751== ----------------------------------------------------------------
==27751==
==27751== Possible data race during write of size 8 at 0xA6A6E18 by thread #1
==27751== Locks held: none
==27751==    at 0x40187C: ~_Impl_base (thread:97)
==27751==    by 0x40187C: std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#2} ()> >::~_Impl() (thread:107)
==27751==    by 0x4014E6: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > > (new_allocator.h:124)
==27751==    by 0x4014E6: _S_destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > > (alloc_traits.h:281)
==27751==    by 0x4014E6: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > > (alloc_traits.h:405)
==27751==    by 0x4014E6: std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#2} ()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#2} ()> > >, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:407)
==27751==    by 0x401ED5: _M_release (shared_ptr_base.h:144)
==27751==    by 0x401ED5: ~__shared_count (shared_ptr_base.h:546)
==27751==    by 0x401ED5: ~__shared_ptr (shared_ptr_base.h:781)
==27751==    by 0x401ED5: ~shared_ptr (shared_ptr.h:93)
==27751==    by 0x401ED5: thread<Foo::DoSomething() const::__lambda1> (thread:135)
==27751==    by 0x401ED5: Foo::DoSomething() const (foo_main.cpp:20)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==
==27751== This conflicts with a previous read of size 8 by thread #3
==27751== Locks held: none
==27751==    at 0x531B316: ??? (in /usr/lib64/libstdc++.so.6.0.19)
==27751==    by 0x4C357B2: mythread_wrapper (hg_intercepts.c:406)
==27751==    by 0x5051EA4: start_thread (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x7848B0C: clone (in /usr/lib64/libc-2.17.so)
==27751==  Address 0xa6a6e18 is 24 bytes inside a block of size 56 alloc'd
==27751==    at 0x4C2D7B8: operator new(unsigned long) (vg_replace_malloc.c:422)
==27751==    by 0x401DAF: allocate (new_allocator.h:104)
==27751==    by 0x401DAF: allocate (alloc_traits.h:351)
==27751==    by 0x401DAF: __shared_count<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr_base.h:499)
==27751==    by 0x401DAF: __shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr_base.h:957)
==27751==    by 0x401DAF: shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr.h:316)
==27751==    by 0x401DAF: allocate_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr.h:598)
==27751==    by 0x401DAF: make_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr.h:614)
==27751==    by 0x401DAF: _M_make_routine<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (thread:193)
==27751==    by 0x401DAF: thread<Foo::DoSomething() const::__lambda1> (thread:135)
==27751==    by 0x401DAF: Foo::DoSomething() const (foo_main.cpp:20)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==  Block was alloc'd by thread #1
==27751==
==27751== ----------------------------------------------------------------
==27751==
==27751== Possible data race during read of size 8 at 0xA6A6E28 by thread #1
==27751== Locks held: none
==27751==    at 0x40187F: ~__shared_count (shared_ptr_base.h:545)
==27751==    by 0x40187F: ~__shared_ptr (shared_ptr_base.h:781)
==27751==    by 0x40187F: ~shared_ptr (shared_ptr.h:93)
==27751==    by 0x40187F: ~_Impl_base (thread:97)
==27751==    by 0x40187F: std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#2} ()> >::~_Impl() (thread:107)
==27751==    by 0x4014E6: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > > (new_allocator.h:124)
==27751==    by 0x4014E6: _S_destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > > (alloc_traits.h:281)
==27751==    by 0x4014E6: destroy<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > > (alloc_traits.h:405)
==27751==    by 0x4014E6: std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#2} ()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::{lambda()#2} ()> > >, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:407)
==27751==    by 0x401ED5: _M_release (shared_ptr_base.h:144)
==27751==    by 0x401ED5: ~__shared_count (shared_ptr_base.h:546)
==27751==    by 0x401ED5: ~__shared_ptr (shared_ptr_base.h:781)
==27751==    by 0x401ED5: ~shared_ptr (shared_ptr.h:93)
==27751==    by 0x401ED5: thread<Foo::DoSomething() const::__lambda1> (thread:135)
==27751==    by 0x401ED5: Foo::DoSomething() const (foo_main.cpp:20)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==
==27751== This conflicts with a previous write of size 8 by thread #3
==27751== Locks held: none
==27751==    at 0x531B325: ??? (in /usr/lib64/libstdc++.so.6.0.19)
==27751==    by 0x4C357B2: mythread_wrapper (hg_intercepts.c:406)
==27751==    by 0x5051EA4: start_thread (in /usr/lib64/libpthread-2.17.so)
==27751==    by 0x7848B0C: clone (in /usr/lib64/libc-2.17.so)
==27751==  Address 0xa6a6e28 is 40 bytes inside a block of size 56 alloc'd
==27751==    at 0x4C2D7B8: operator new(unsigned long) (vg_replace_malloc.c:422)
==27751==    by 0x401DAF: allocate (new_allocator.h:104)
==27751==    by 0x401DAF: allocate (alloc_traits.h:351)
==27751==    by 0x401DAF: __shared_count<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr_base.h:499)
==27751==    by 0x401DAF: __shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr_base.h:957)
==27751==    by 0x401DAF: shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr.h:316)
==27751==    by 0x401DAF: allocate_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> >, std::allocator<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr.h:598)
==27751==    by 0x401DAF: make_shared<std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> >, std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (shared_ptr.h:614)
==27751==    by 0x401DAF: _M_make_routine<std::_Bind_simple<Foo::DoSomething() const::__lambda1()> > (thread:193)
==27751==    by 0x401DAF: thread<Foo::DoSomething() const::__lambda1> (thread:135)
==27751==    by 0x401DAF: Foo::DoSomething() const (foo_main.cpp:20)
==27751==    by 0x401480: main (foo_main.cpp:38)
==27751==  Block was alloc'd by thread #1
==27751==
==27751==
==27751== Use --history-level=approx or =none to gain increased speed, at
==27751== the cost of reduced accuracy of conflicting-access information
==27751== For lists of detected and suppressed errors, rerun with: -s
==27751== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 9 from 8)

When accessing a member function from multiple threads do I always need to lock BEFORE attempting access through the "this" pointer? Since it is a const member function it won't modify the object itself so I wouldn't think there would be an issue but valgrind is still giving me a lot of warnings.

In the real application I am working on the member function being called by the threads does quite a lot, so moving the code out of the member function and copying it everywhere I use it isn't really an option. But locking before even calling the function poses a problem because a lot of what is in the function could be done concurrently and preventing the portions of the function that don't use shared resources from running concurrently would significantly slow down the application.

1 Answers

This does look a bit like an old bug in libstdc++. This is the output that I get.

==27663== This conflicts with a previous read of size 8 by thread #3
==27663== Locks held: none
==27663==    at 0x4AD9056: ??? (in /usr/lib64/libstdc++.so.6.0.19)
==27663==    by 0x40394FD: mythread_wrapper (hg_intercepts.c:406)
==27663==    by 0x524ADD4: start_thread (in /usr/lib64/libpthread-2.17.so)
==27663==    by 0x555CEAC: clone (in /usr/lib64/libc-2.17.so)

That looks like a raw read of memory. I don't have libstdc++ debuginfo so I can't tell where the '???' is. The callstack is in the new thread so I guess it's libstdc++ preparing to call the lambda.

Back in main, for the std::thread creation and shutdown, I see that libstc++ has switched from using std::shared_ptr to std::unique_ptr.

I'm pretty sure that the libstdc++ code is

   b5048:       eb d3                   jmp    b501d <_ZNSt11this_thread11__sleep_forENSt6chrono8durationIlSt5ratioILl1ELl1EEEENS1_IlS2_ILl1ELl1000000000EEEE@@GLIBCXX_3.4.18+0x16d>
   b504a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Entry point here:
   b5050:       55                      push   %rbp
   b5051:       53                      push   %rbx
   b5052:       48 83 ec 08             sub    $0x8,%rsp

Error here:
   b5056:       48 8b 07                mov    (%rdi),%rax
   b5059:       48 8b 5f 10             mov    0x10(%rdi),%rbx
   b505d:       48 c7 47 08 00 00 00    movq   $0x0,0x8(%rdi)
   b5064:       00
   b5065:       48 c7 47 10 00 00 00    movq   $0x0,0x10(%rdi)
   b506c:       00
   b506d:       ff 50 10                call   *0x10(%rax)
   b5070:       48 85 db                test   %rbx,%rbx
   b5073:       74 20                   je     b5095 <_ZNSt11this_thread11__sleep_forENSt6chrono8durationIlSt5ratioILl1ELl1EEEENS1_IlS2_ILl1ELl1000000000EEEE@@GLIBCXX_3.4.18+0x1e5>

The pointer is:

(gdb) p this
$80 = (std::thread::_Impl<std::_Bind_simple<Foo::DoSomething() const::__lambda0()> > * const) 0x582c058

That's the template class implementing the std::thread internal state implementation.

Related