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.