TSAN shows data race in ~std::shared_ptr

Does the following code have a race or is it a false positive? If it's a false positive, can I teach TSAN about it?

#include <memory>
#include <thread>
#include <iostream>

int main()
{
    int v1 = 0;
    int v2 = 0;
    std::thread t1;
    std::thread t2;

    {
       auto thingy = std::make_shared<int>(1);
       t1 = std::thread([thingy, &v1] { v1 = *thingy; });
       t2 = std::thread([thingy, &v2] { v2 = *thingy; });
    }

    t1.join();
    t2.join();
    std::cout << v1 << "\n" << v2 << "\n";
}

Compiled with:
clang++ -std=c++11 -stdlib=libc++ -pthread -lc++abi -fsanitize=thread shared_ptr_thread_safety.cpp -o shared_ptr_thread_safety

clang version 3.4 (tags/RELEASE_34/final)

Hi Ben,

Thanks for the test case!

You’ve hit this entry of our FAQ:
https://code.google.com/p/thread-sanitizer/wiki/CppManual#FAQ

Hi Ben,

Thanks for the test case!
You've hit this entry of our FAQ:
https://code.google.com/p/thread-sanitizer/wiki/CppManual#FAQ

  * Q: I see what looks like a false report inside the libstdc++
    (libc++) code.

This may happen in c++11 mode. We are not aware of any such case today,
but ThreadSanitizer is not heavily tested on code that uses c++11
threading. A likely solution would be to rebuild libstdc++ (libc++) with
ThreadSanitizer (this might be tricky, and the process changes
periodically; contact us for details). It is also possible that
libstdc++ (libc++) has a bug, we've seen at least one such
<http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59215> before.

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

Aha, I think I read this a while ago, but it didn't click.

Apparently, we now know about one more such case and we'll need to
update the FAQ. :slight_smile:

Nice.

Good news is that this is very easy to fix: just rebuild libc++ with tsan
by changing the flags in projects/libcxx/lib/buildit.

Fair enough. I'm using cmake to build, so I added the flag with -DLIBCXX_CXX_FEATURE_FLAGS=-fsanitize=thread

I'm getting some undefined symbol: __tsan_init whilst building anything.

If I use the instrumented libc++, would it not have a negative performance impact on every application that uses it? Should I build it with a different name and then use -nostdlib -lc++-tsan in my tsan specific build? I guess I could even add libc++ to my projects cmake build and link to the one I built as part of that.

I've just verified that using instrumented libc++ eliminates the tsan
reports.

Nice.

If curious, this is happening because shared_ptr<int>::~shared_ptr has
atomic synchronization in it
and if tsan does not observe that synchronization it can not properly
reason about races.

Seems reasonable.

Thanks,

Ben

Fair enough. I'm using cmake to build, so I added the flag with
-DLIBCXX_CXX_FEATURE_FLAGS=-fsanitize=thread

I afraid this is not going to work. tsan-instrumented libc++.so can be used
only with tsan-instrumented binaries.
With non-instrumented binaries you will be getting link time errors (just
like you did).
BTW, I filed a bug for better build/test system support:
https://code.google.com/p/thread-sanitizer/issues/detail?id=48

--kcc

I'm getting some undefined symbol: __tsan_init whilst building anything.

    Fair enough. I'm using cmake to build, so I added the flag with
    -DLIBCXX_CXX_FEATURE_FLAGS=-__fsanitize=thread

I afraid this is not going to work. tsan-instrumented libc++.so can be
used only with tsan-instrumented binaries.
With non-instrumented binaries you will be getting link time errors
(just like you did).

Looks like they were due to my clang being linked against my newly installed, instrumented libc++. I've renamed that one to libc++tsan.so and reinstated the uninstrumented libc++.so.

BTW, I filed a bug for better build/test system support:
https://code.google.com/p/thread-sanitizer/issues/detail?id=48

Yeah, I think this is going to be useful.

Not sure why I can't link against my new libc++-tsan though:

$ clang++-3.5 -fsanitize=thread -std=c++11 -stdlib=libc++ shared_ptr_thread_safety.cpp -nodefaultlibs -lc++-tsan -lc++abi -lm -lc -lgcc_s -lgcc -o shared_ptr_thread_safety

$ ldd shared_ptr_thread_safety
  linux-vdso.so.1 => (0x00007fff2a3fe000)
  libc++.so.1 => /usr/lib/libc++.so.1 (0x00007f761245e000)
  libc++abi.so.1 => /usr/lib/libc++abi.so.1 (0x00007f761220f000)
  libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7611f0a000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7611b42000)
  libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f761192c000)
  libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f761170e000)
  librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f7611506000)
  libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7611302000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f7613847000)

Ben

    Fair enough. I'm using cmake to build, so I added the flag with
    -DLIBCXX_CXX_FEATURE_FLAGS=-__fsanitize=thread

I afraid this is not going to work. tsan-instrumented libc++.so can be
used only with tsan-instrumented binaries.
With non-instrumented binaries you will be getting link time errors
(just like you did).

Looks like they were due to my clang being linked against my newly
installed, instrumented libc++. I've renamed that one to libc++tsan.so and
reinstated the uninstrumented libc++.so.

BTW, I filed a bug for better build/test system support:

https://code.google.com/p/thread-sanitizer/issues/detail?id=48

Yeah, I think this is going to be useful.

Not sure why I can't link against my new libc++-tsan though:

$ clang++-3.5 -fsanitize=thread -std=c++11 -stdlib=libc++
shared_ptr_thread_safety.cpp -nodefaultlibs -lc++-tsan -lc++abi -lm -lc
-lgcc_s -lgcc -o shared_ptr_thread_safety

These are the command parameters I used to check your test case:
# LIBCXX_ROOT -- directory where I've built tsan-instrumented libc++
clang -g -I$LIBCXX_ROOT/include -I$LIBCXX_ROOT/test/support
$LIBCXX_ROOT/lib/libc++.so -Wl,-R$LIBCXX_ROOT/lib benpope.cc -std=c++11
-fsanitize=thread