https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78755

            Bug ID: 78755
           Summary: Thread sanitizer reports race with std::promise with
                    -O0, works with -O{1,2,3}
           Product: gcc
           Version: 6.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: sanitizer
          Assignee: unassigned at gcc dot gnu.org
          Reporter: csaba_22 at yahoo dot co.uk
                CC: dodji at gcc dot gnu.org, dvyukov at gcc dot gnu.org,
                    jakub at gcc dot gnu.org, kcc at gcc dot gnu.org
  Target Milestone: ---

Hand-built GCC on Ubuntu 14.04LTS.

$ g++-620 -v
Using built-in specs.
COLLECT_GCC=g++-620
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/6.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../gcc-6.2.0/configure --program-suffix=-620
--enable-version-specific-runtime-libs --enable-languages=c,c++,lto
Thread model: posix
gcc version 6.2.0 (GCC) 

Source:
#include <thread>
#include <future>
#include <atomic>
#include <memory>
#include <iostream>

class SD {
public:
    ~SD()
    {
        stop();
    }
    //
    void start();
    void stop();
    void operator()();
private:
    std::unique_ptr<std::thread> thread_;        ///< The thread object.
    std::promise<void>           promise_;       ///< The synchronization
promise.
    std::atomic_bool             bored_{false};  ///< Loop exit flag
};

void SD::start()
{
    thread_.reset(new std::thread{std::ref(*this)});
    // Indicating that the thread_ object can be used.
    promise_.set_value(); // data race: write
}

void SD::stop()
{
    if (thread_) { // it's running
        bored_ = true; // tell it to stop
        thread_->join(); // wait for it to stop
    }
}

void SD::operator ()()
{
    // Making sure that the thread_ object has been set properly.
    promise_.get_future().wait(); // data race: read

    while (!bored_) {
        std::cout << (bored_ ? "   " : "not") << " bored\n";
        std::this_thread::sleep_for(std::chrono::seconds{1});
    }
}

int main()
{
    SD s;
    std::cout << "Thread created\n";
    s.start();
    std::cout << "Thread is running\n";
    std::this_thread::sleep_for(std::chrono::seconds{2});
    std::cout << "Goodbye\n";
}

$ g++-620 -fsanitize=thread -g -O0 -pthread -std=gnu++0x -Wall -pedantic
-Wextra -Wformat=2 -I ~/workspace/zarquon -o "promise" "promise-std.cc"
-lboost_thread -lboost_system -save-temps
(no errors)
$ ./promise
Thread created
Thread is running
==================
WARNING: ThreadSanitizer: data race (pid=23398)
  Read of size 8 at 0x7d0c0000efe8 by thread T1:
    #0 std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::get() const
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/unique_ptr.h:305
(promise+0x000000405d59)
    #1 std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::operator*() const
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/unique_ptr.h:291
(promise+0x000000404334)
    #2 std::__future_base::_State_baseV2::wait()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/future:329
(promise+0x00000040343b)
    #3 std::__basic_future<void>::wait() const
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/future:662
(promise+0x0000004056b4)
    #4 SD::operator()() /tmp/promise-std.cc:41 (promise+0x0000004024f5)
    #5 void std::__invoke_impl<void, SD&>(std::__invoke_other, SD&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:218
(promise+0x0000004091f7)
    #6 std::result_of<SD& ()>::type std::__invoke<SD&>(SD&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:260
(promise+0x0000004091a6)
    #7 std::result_of<SD& ()>::type std::reference_wrapper<SD>::operator()<>()
const /usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:474
(promise+0x000000409108)
    #8 void std::_Bind_simple<std::reference_wrapper<SD>
()>::_M_invoke<>(std::_Index_tuple<>)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:1400
(promise+0x0000004090b8)
    #9 std::_Bind_simple<std::reference_wrapper<SD> ()>::operator()()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:1389
(promise+0x000000408f91)
    #10 std::thread::_State_impl<std::_Bind_simple<std::reference_wrapper<SD>
()> >::_M_run()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/thread:196
(promise+0x000000408ed8)
    #11 execute_native_thread_routine
../../../../../gcc-6.1.0/libstdc++-v3/src/c++11/thread.cc:83
(libstdc++.so.6+0x0000000b8901)

  Previous write of size 8 at 0x7d0c0000efe8 by main thread:
    #0
std::enable_if<std::__and_<std::is_move_constructible<std::__future_base::_Result_base*>,
std::is_move_assignable<std::__future_base::_Result_base*> >::value,
void>::type
std::swap<std::__future_base::_Result_base*>(std::__future_base::_Result_base*&,
std::__future_base::_Result_base*&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/move.h:191
(promise+0x000000407f6e)
    #1 std::_Tuple_impl<0ul, std::__future_base::_Result_base*,
std::__future_base::_Result_base::_Deleter>::_M_swap(std::_Tuple_impl<0ul,
std::__future_base::_Result_base*,
std::__future_base::_Result_base::_Deleter>&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/tuple:331
(promise+0x000000407bd4)
    #2 std::tuple<std::__future_base::_Result_base*,
std::__future_base::_Result_base::_Deleter>::swap(std::tuple<std::__future_base::_Result_base*,
std::__future_base::_Result_base::_Deleter>&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/tuple:1215
(promise+0x0000004070aa)
    #3 void std::swap<std::__future_base::_Result_base*,
std::__future_base::_Result_base::_Deleter>(std::tuple<std::__future_base::_Result_base*,
std::__future_base::_Result_base::_Deleter>&,
std::tuple<std::__future_base::_Result_base*,
std::__future_base::_Result_base::_Deleter>&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/tuple:1548
(promise+0x000000405fd2)
    #4 std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::swap(std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/unique_ptr.h:352
(promise+0x0000004048fa)
    #5
std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/future:538
(promise+0x000000403885)
    #6 void std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*
const&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*,
bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*
const&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:235
(promise+0x000000408771)
    #7 std::result_of<void (std::__future_base::_State_baseV2::*
const&(std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&,
bool*&&))(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)>::type
std::__invoke<void (std::__future_base::_State_baseV2::*
const&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void
(std::__future_base::_State_baseV2::*
const&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:260
(promise+0x0000004081fb)
    #8 decltype (__invoke((*this)._M_pmf,
(forward<std::__future_base::_State_baseV2*>)({parm#1}),
(forward<std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*>)({parm#1}),
(forward<bool*>)({parm#1}))) std::_Mem_fn_base<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
true>::operator()<std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*,
bool*>(std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) const
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:613
(promise+0x000000407ecf)
    #9 void std::_Bind_simple<std::_Mem_fn<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)>
(std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)>::_M_invoke<0ul, 1ul,
2ul>(std::_Index_tuple<0ul, 1ul, 2ul>)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:1400
(promise+0x000000407b6a)
    #10 std::_Bind_simple<std::_Mem_fn<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)>
(std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)>::operator()()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/functional:1389
(promise+0x000000407057)
    #11 void std::__once_call_impl<std::_Bind_simple<std::_Mem_fn<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)>
(std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*)> >()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/mutex:587
(promise+0x000000405f82)
    #12 pthread_once
../../../../gcc-6.1.0/libsanitizer/tsan/tsan_interceptors.cc:1304
(libtsan.so.0+0x0000000294af)
    #13 __gthread_once
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/x86_64-pc-linux-gnu/bits/gthr-default.h:699
(promise+0x000000402190)
    #14 void std::call_once<void
(std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void
(std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*),
std::__future_base::_State_baseV2*&&,
std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/mutex:619
(promise+0x00000040471a)
    #15
std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>, bool)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/future:393
(promise+0x00000040353c)
    #16 std::promise<void>::set_value()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/future:1308
(promise+0x000000403d82)
    #17 SD::start() /tmp/promise-std.cc:27 (promise+0x000000402411)
    #18 main /tmp/promise-std.cc:53 (promise+0x0000004025f7)

  Location is heap block of size 48 at 0x7d0c0000efd0 allocated by main thread:
    #0 operator new(unsigned long)
../../../../gcc-6.1.0/libsanitizer/tsan/tsan_new_delete.cc:43
(libtsan.so.0+0x000000065833)
    #1
__gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>, (__gnu_cxx::_Lock_policy)2>
>::allocate(unsigned long, void const*)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/ext/new_allocator.h:104
(promise+0x000000408a1f)
    #2
std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>, (__gnu_cxx::_Lock_policy)2>
>
>::allocate(std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>, (__gnu_cxx::_Lock_policy)2>
>&, unsigned long)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/alloc_traits.h:416
(promise+0x0000004087e5)
    #3
std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>, (__gnu_cxx::_Lock_policy)2>
> >
std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>, (__gnu_cxx::_Lock_policy)2>
>
>(std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>, (__gnu_cxx::_Lock_policy)2>
>&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/allocated_ptr.h:103
(promise+0x000000408307)
    #4
std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>>(std::_Sp_make_shared_tag,
std::__future_base::_State_baseV2*,
std::allocator<std::__future_base::_State_baseV2> const&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/shared_ptr_base.h:613
(promise+0x00000040806f)
    #5 std::__shared_ptr<std::__future_base::_State_baseV2,
(__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<std::__future_base::_State_baseV2>>(std::_Sp_make_shared_tag,
std::allocator<std::__future_base::_State_baseV2> const&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/shared_ptr_base.h:1100
(promise+0x000000407c54)
    #6
std::shared_ptr<std::__future_base::_State_baseV2>::shared_ptr<std::allocator<std::__future_base::_State_baseV2>>(std::_Sp_make_shared_tag,
std::allocator<std::__future_base::_State_baseV2> const&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/shared_ptr.h:319
(promise+0x000000407136)
    #7 std::shared_ptr<std::__future_base::_State_baseV2>
std::allocate_shared<std::__future_base::_State_baseV2,
std::allocator<std::__future_base::_State_baseV2>>(std::allocator<std::__future_base::_State_baseV2>
const&)
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/shared_ptr.h:620
(promise+0x0000004060ec)
    #8 std::shared_ptr<std::__future_base::_State_baseV2>
std::make_shared<std::__future_base::_State_baseV2>()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/shared_ptr.h:636
(promise+0x000000404b2a)
    #9 std::promise<void>::promise()
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/future:1223
(promise+0x000000403ac2)
    #10 SD::SD() /tmp/promise-std.cc:7 (promise+0x000000403fbc)
    #11 main /tmp/promise-std.cc:51 (promise+0x0000004025dc)

  Thread T1 (tid=23400, running) created by main thread at:
    #0 pthread_create
../../../../gcc-6.1.0/libsanitizer/tsan/tsan_interceptors.cc:876
(libtsan.so.0+0x000000027bfd)
    #1 __gthread_create
/home/csabaraduly/workspace/GCC61/__build_with_isl/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:662
(libstdc++.so.6+0x0000000b8bf4)
    #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State,
std::default_delete<std::thread::_State> >, void (*)())
../../../../../gcc-6.1.0/libstdc++-v3/src/c++11/thread.cc:163
(libstdc++.so.6+0x0000000b8bf4)
    #3 SD::start() /tmp/promise-std.cc:25 (promise+0x0000004023f2)
    #4 main /tmp/promise-std.cc:53 (promise+0x0000004025f7)

SUMMARY: ThreadSanitizer: data race
/usr/local/lib/gcc/x86_64-pc-linux-gnu/6.2.0/include/c++/bits/unique_ptr.h:305
in std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::get() const
==================
not bored
not bored
Goodbye
ThreadSanitizer: reported 1 warnings

If compiled with -O1 or higher, the program runs without a complaint.

Reply via email to