Skip to content

Commit 5397c2e

Browse files
committed
[libc++] Fix std::future not waiting until the thread is finished to clean up
1 parent 6e57326 commit 5397c2e

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

libcxx/include/future

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,7 @@ void __async_assoc_state<_Rp, _Fp>::__execute() {
865865

866866
template <class _Rp, class _Fp>
867867
void __async_assoc_state<_Rp, _Fp>::__on_zero_shared() _NOEXCEPT {
868-
if (base::__state_ & base::__constructed)
869-
this->wait();
868+
this->wait();
870869
base::__on_zero_shared();
871870
}
872871

@@ -903,8 +902,7 @@ void __async_assoc_state<void, _Fp>::__execute() {
903902

904903
template <class _Fp>
905904
void __async_assoc_state<void, _Fp>::__on_zero_shared() _NOEXCEPT {
906-
if (base::__state_ & base::__constructed)
907-
this->wait();
905+
this->wait();
908906
base::__on_zero_shared();
909907
}
910908

@@ -1831,7 +1829,12 @@ template <class _Rp, class _Fp>
18311829
_LIBCPP_HIDE_FROM_ABI future<_Rp> __make_async_assoc_state(_Fp&& __f) {
18321830
unique_ptr<__async_assoc_state<_Rp, _Fp>, __release_shared_count> __h(
18331831
new __async_assoc_state<_Rp, _Fp>(std::forward<_Fp>(__f)));
1834-
std::thread(&__async_assoc_state<_Rp, _Fp>::__execute, __h.get()).detach();
1832+
try {
1833+
std::thread(&__async_assoc_state<_Rp, _Fp>::__execute, __h.get()).detach();
1834+
} catch (...) {
1835+
__h->__make_ready();
1836+
throw;
1837+
}
18351838
return future<_Rp>(__h.get());
18361839
}
18371840

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// UNSUPPORTED: no-threads
10+
// UNSUPPORTED: c++03
11+
12+
// Make sure that the `future` destructor keeps the data alive until the thread finished. This test fails by triggering
13+
// TSan. It may not be observable by normal means.
14+
15+
#include <atomic>
16+
#include <future>
17+
#include <mutex>
18+
19+
std::mutex mux;
20+
21+
int main() {
22+
using namespace std::chrono_literals;
23+
mux.lock();
24+
std::atomic<bool> in_async = false;
25+
auto v = std::async(std::launch::async, [&in_async, value = 1]() mutable {
26+
in_async = true;
27+
in_async.notify_all();
28+
std::scoped_lock thread_lock(mux);
29+
value = 4;
30+
(void)value;
31+
});
32+
in_async.wait(true);
33+
mux.unlock();
34+
}

0 commit comments

Comments
 (0)