Skip to content

Commit a2306a7

Browse files
committed
[libc++] fix counting_semaphore lost wakeups
1 parent df4ba00 commit a2306a7

File tree

3 files changed

+92
-10
lines changed

3 files changed

+92
-10
lines changed

libcxx/include/__atomic/atomic_sync.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile*, __cxx_contention
4646
template <class _Atp, class _Fn>
4747
struct __libcpp_atomic_wait_backoff_impl {
4848
_Atp* __a;
49-
_Fn __test_fn;
49+
_Fn __test_with_old;
5050
_LIBCPP_AVAILABILITY_SYNC
5151
_LIBCPP_HIDE_FROM_ABI bool operator()(chrono::nanoseconds __elapsed) const {
5252
if (__elapsed > chrono::microseconds(64)) {
53-
auto const __monitor = std::__libcpp_atomic_monitor(__a);
54-
if (__test_fn())
53+
auto __monitor = std::__libcpp_atomic_monitor(__a);
54+
if (__test_with_old(__monitor))
5555
return true;
5656
std::__libcpp_atomic_wait(__a, __monitor);
5757
} else if (__elapsed > chrono::microseconds(4))
@@ -62,9 +62,17 @@ struct __libcpp_atomic_wait_backoff_impl {
6262
}
6363
};
6464

65+
template <class _Atp, class _Fn, class _Fn2>
66+
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
67+
__cxx_atomic_wait(_Atp* __a, _Fn&& __test_fn, _Fn2&& __test_with_old) {
68+
__libcpp_atomic_wait_backoff_impl<_Atp, __decay_t<_Fn2> > __backoff_fn = {__a, __test_with_old};
69+
return std::__libcpp_thread_poll_with_backoff(__test_fn, __backoff_fn);
70+
}
71+
6572
template <class _Atp, class _Fn>
6673
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_wait(_Atp* __a, _Fn&& __test_fn) {
67-
__libcpp_atomic_wait_backoff_impl<_Atp, __decay_t<_Fn> > __backoff_fn = {__a, __test_fn};
74+
auto __test_with_old = [&](auto&) { return __test_fn(); };
75+
__libcpp_atomic_wait_backoff_impl<_Atp, decltype(__test_with_old)> __backoff_fn{__a, __test_with_old};
6876
return std::__libcpp_thread_poll_with_backoff(__test_fn, __backoff_fn);
6977
}
7078

libcxx/include/semaphore

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ using binary_semaphore = counting_semaphore<1>;
5454
#include <__assert> // all public C++ headers provide the assertion handler
5555
#include <__atomic/atomic_base.h>
5656
#include <__atomic/atomic_sync.h>
57+
#include <__atomic/contention_t.h>
5758
#include <__atomic/memory_order.h>
5859
#include <__availability>
5960
#include <__chrono/time_point.h>
@@ -95,19 +96,22 @@ public:
9596
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
9697
__update <= _LIBCPP_SEMAPHORE_MAX - __old, "update is greater than the expected value");
9798

98-
if (__old > 0) {
99-
// Nothing to do
100-
} else if (__update > 1)
99+
if (__old == 0) {
101100
__a_.notify_all();
102-
else
103-
__a_.notify_one();
101+
}
104102
}
105103
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void acquire() {
106104
auto const __test_fn = [this]() -> bool {
107105
auto __old = __a_.load(memory_order_relaxed);
108106
return (__old != 0) && __a_.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
109107
};
110-
__cxx_atomic_wait(&__a_.__a_, __test_fn);
108+
auto const __test_with_old = [this](__cxx_contention_t& __monitor) -> bool {
109+
ptrdiff_t __old = __monitor;
110+
bool __r = __try_acquire_impl(__old);
111+
__monitor = static_cast<__cxx_contention_t>(__old);
112+
return __r;
113+
};
114+
__cxx_atomic_wait(&__a_.__a_, __test_fn, __test_with_old);
111115
}
112116
template <class _Rep, class _Period>
113117
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
@@ -119,6 +123,11 @@ public:
119123
}
120124
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool try_acquire() {
121125
auto __old = __a_.load(memory_order_acquire);
126+
return __try_acquire_impl(__old);
127+
}
128+
129+
private:
130+
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY bool __try_acquire_impl(ptrdiff_t& __old) {
122131
while (true) {
123132
if (__old == 0)
124133
return false;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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: libcpp-has-no-threads
10+
// UNSUPPORTED: c++03, c++11, c++14, c++17
11+
12+
// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0.
13+
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}
14+
15+
// This is a regression test for https://llvm.org/PR47013.
16+
17+
// <semaphore>
18+
19+
#include <barrier>
20+
#include <semaphore>
21+
#include <thread>
22+
#include <vector>
23+
24+
#include "make_test_thread.h"
25+
26+
static std::counting_semaphore<> s(0);
27+
static std::barrier<> b(8 + 1);
28+
29+
void acquire() {
30+
for (int i = 0; i < 10'000; ++i) {
31+
s.acquire();
32+
b.arrive_and_wait();
33+
}
34+
}
35+
36+
void release() {
37+
for (int i = 0; i < 10'000; ++i) {
38+
s.release(1);
39+
s.release(1);
40+
s.release(1);
41+
s.release(1);
42+
43+
s.release(1);
44+
s.release(1);
45+
s.release(1);
46+
s.release(1);
47+
48+
b.arrive_and_wait();
49+
}
50+
}
51+
52+
int main(int, char**) {
53+
for (int run = 0; run < 20; ++run) {
54+
std::vector<std::thread> threads;
55+
for (int i = 0; i < 8; ++i)
56+
threads.push_back(support::make_test_thread(acquire));
57+
58+
threads.push_back(support::make_test_thread(release));
59+
60+
for (auto& thread : threads)
61+
thread.join();
62+
}
63+
64+
return 0;
65+
}

0 commit comments

Comments
 (0)