Domanda

To experiment with the thread-sanitizer, I created a tiny C++ program which by purpose contains a data race. Indeed, tsan does detect the error, great! However I am puzzled by the generated message...

  1. It reports a write-write data race, where I would have expected a read-write race. I would have hoped that the find() does not write in my container. If I do a further small code adjustment trying to get a const version of set::find(), the same write-write race seems to remain.
  2. It shows a write conflict between a 4-byte atomic write and an 8-byte write at the same address. It seems weird that the same field in the container class is accessed by two such different access types.

Is there an option to use a const find() that does not write into the STL container?

This is the tested C++ program:

/*****************************************************************************
 * Small example with an inter-thread data race that is not obvious.
 * the error is a consequence of the non-threadsafeness of the STL containers.
 * Threading is created through portable C++11 constructs.
 * Tsan does detect the data race(?).
 *
 * Compile with one of:
 * g++-4.8 -std=c++11 -g -Wall -o race-stl11b race-stl11b.cc -pthread
 * g++-4.8 -std=c++11 -g -Wall -fsanitize=thread -fPIE -o race-stl11b-tsan race-stl11b.cc -ltsan -pie -pthread
 ******************************************************************************/

#include <iostream>
#include <thread>
#include <set>

int main()
{
    // create an empty bucket
    std::set<int> bucket;

    // Use a background task to insert value '5' in the bucket 
    std::thread t([&](){ bucket.insert(5); });

    // Check if value '3' is in the bucket (not expected :-)
    bool contains3 = bucket.find(3) != bucket.cend();
    std::cout << "Foreground find done: " << contains3 << std::endl;

    // Wait for the background thread to finish
    t.join();

    // verify that value '5' did arrive in the bucket
    bool contains5 = bucket.find(5) != bucket.cend();
    std::cout << "Background insert: " << contains5 << std::endl;

    return 0;
}

And this is (part of) the tsan output:

WARNING: ThreadSanitizer: data race (pid=21774)                                                                                               

  Write of size 8 at 0x7d080000bfc8 by thread T1:                                                                                             
    #0 <null> <null>:0 (libtsan.so.0+0x00000001e2c0)                                                                                          
    #1 deallocate /usr/include/c++/4.8/ext/new_allocator.h:110 (exe+0x000000002a79)                                                           
    #2 deallocate /usr/include/c++/4.8/bits/alloc_traits.h:377 (exe+0x000000002962)                                                           
    #3 _M_destroy /usr/include/c++/4.8/bits/shared_ptr_base.h:417 (exe+0x00000000306b)                                                        
    #4 <null> <null>:0 (libstdc++.so.6+0x0000000b5f8a)                                                                                        

  Previous atomic write of size 4 at 0x7d080000bfc8 by main thread:
    #0 <null> <null>:0 (libtsan.so.0+0x00000000da45)
    #1 __exchange_and_add /usr/include/c++/4.8/ext/atomicity.h:49 (exe+0x000000001c9f)
    #2 __exchange_and_add_dispatch /usr/include/c++/4.8/ext/atomicity.h:82 (exe+0x000000001d56)
    #3 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/4.8/bits/shared_ptr_base.h:141 (exe+0x00000000390d)
    #4 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/4.8/bits/shared_ptr_base.h:553 (exe+0x00000000363c)
    #5 std::__shared_ptr<std::thread::_Impl_base, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/4.8/bits/shared_ptr_base.h:810
 (exe+0x00000000351b)
    #6 std::shared_ptr<std::thread::_Impl_base>::~shared_ptr() /usr/include/c++/4.8/bits/shared_ptr.h:93 (exe+0x000000003547)
    #7 thread<main()::__lambda0> /usr/include/c++/4.8/thread:135 (exe+0x0000000020c3)
    #8 main /home/......./race-stl11b.cc:22 (exe+0x000000001e38)

Thanks for any feedback, Jos

È stato utile?

Soluzione

It looks like ThreadSanitizer is giving you a false positive on the std::thread implementation.

Reducing your example to not do any set manipulations, like the following:

#include <iostream>
#include <thread>
#include <set>

int main()
{
    std::set<int> bucket;
    std::thread t([&](){ /*bucket.insert(5);*/ });
    t.join();

    return 0;
}

Still gives the same error in ThreadSanitizer.

Note that ThreadSanitizer does NOT find your read-write race condition.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top