Skip to content

[libcxx] Avoid a busy-loop in atomic's lost_wakeup.pass.cpp - #211242

Merged
Zingam merged 4 commits into
llvm:mainfrom
LucasChollet:libcxx-atomic-wakeup
Sep 19, 2026
Merged

Zingam merged 4 commits into
llvm:mainfrom
LucasChollet:libcxx-atomic-wakeup

Conversation

@LucasChollet

@LucasChollet LucasChollet commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #207763
On single-core or heavily loaded systems, using this_thread::yield()
prevents the notifier from burning all its allocated time on the
busy-loop.

The measurements were done on my Linux box, the time is the one reported
by lit. To isolate the test on a single core I used the following
commands:

export LIT_FILTER="wait/lost_wakeup.pass.cpp"
taskset -c 0 ninja -C build check-cxx

Configuration Linux futex Fallback
Normal Pinned core Normal Pinned core
Before 1.50s 8.44s 1.57s 301.02s
With yield 1.73s 7.36s 1.64s 8.88s

Both configurations are with the run < 10 change.

Using __libcpp_thread_poll_with_backoff brings two improvements. First,
in case of a lost wake-up, we get an assertion failure instead of a
hang. And second, on single-core or heavily loaded systems, the notifier
yield instead of burning all its allocated time on the busy-loop.

The measurements were done on my Linux box, the time is the one reported
by lit. To isolate the test on a single core I used the following
commands:

export LIT_FILTER="wait/lost_wakeup.pass.cpp"
taskset -c 0 ninja -C build check-cxx

| Configuration  | Linux futex |             | Fallback |             |
| -------------- | ----------- | ----------- | -------- | ----------- |
|                | Normal      | Pinned core | Normal   | Pinned core |
| Before         | 1.20s       | 15.54s      | 1.26s    | 601.25s     |
| With backoff   | 1.11s       | 14.42s      | 1.15s    | 17.60s      |
@LucasChollet
LucasChollet requested a review from a team as a code owner July 22, 2026 12:05
@llvmorg-github-actions llvmorg-github-actions Bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 22, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-libcxx

Author: Lucas Chollet (LucasChollet)

Changes

Using __libcpp_thread_poll_with_backoff brings two improvements. First, in case of a lost wake-up, we get an assertion failure instead of a hang. And second, on single-core or heavily loaded systems, the notifier yield instead of burning all its allocated time on the busy-loop.

The measurements were done on my Linux box, the time is the one reported by lit. To isolate the test on a single core I used the following commands:

export LIT_FILTER="wait/lost_wakeup.pass.cpp"
taskset -c 0 ninja -C build check-cxx

Configuration Linux futex Fallback
Normal Pinned core Normal Pinned core
Before 1.20s 15.54s 1.26s 601.25s
With backoff 1.11s 14.42s 1.15s 17.60s

Full diff: https://github.com/llvm/llvm-project/pull/211242.diff

1 Files Affected:

  • (modified) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp (+6-2)
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
index 17ad72dd74866..6b486173a4743 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
@@ -14,6 +14,8 @@
 // <atomic>
 
 #include <atomic>
+#include <cassert>
+#include <chrono>
 #include <functional>
 #include <thread>
 #include <vector>
@@ -38,8 +40,10 @@ int main(int, char**) {
 
     auto notify = [&] {
       for (int i = 0; i < num_iterations; ++i) {
-        while (waiter_ready.load() < num_waiters) {
-        }
+        assert(std::__libcpp_thread_poll_with_backoff(
+                   [&]() -> bool { return waiter_ready.load() == num_waiters; },
+                   std::__libcpp_timed_backoff_policy(),
+                   std::chrono::seconds(1)) == std::__poll_with_backoff_results::__poll_success);
         waiter_ready.store(0);
         state.fetch_add(1);
         state.notify_all();

@huixie90 huixie90 self-assigned this Jul 22, 2026
@huixie90

Copy link
Copy Markdown
Member

After discussion offline, I think my opinion is that we should avoid using __libcpp_thread_poll_with_backoff in the test, because

  1. It is an internal implementation detail function
  2. It is a function that implements atomic::wait, and this test is exercising atomic::wait. Using its implementation detail to test itself is not a good idea.

I wonder if we could just use yield (excluding macOS) to see if this help with the test. something like this
#211261

@LucasChollet

Copy link
Copy Markdown
Contributor Author

Updated this PR to follow your suggestion. I also measured the execution time again and updated the table.

auto notify = [&] {
for (int i = 0; i < num_iterations; ++i) {
while (waiter_ready.load() < num_waiters) {
#if !defined(__APPLE__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this about?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From #161086 (comment):

I remember that macOS would put the yield thread on the lowest priority on the scheduler. If the machine was overloaded with other running jobs, our notifier thread would have taken long time to resume again.

@LucasChollet

Copy link
Copy Markdown
Contributor Author

@philnik777 @huixie90 This is ready for review.

@philnik777 philnik777 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really like @huixie90 to take a look at this, since I'm not particularly familiar with this part of the code base.

@huixie90 huixie90 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the comment here suggested that this change does improve testing time on some platforms #211261 (comment). LGTM

@LucasChollet

Copy link
Copy Markdown
Contributor Author

Thanks for the approval! I don't have merge permissions though, could you please press the merge button for me?

@Zingam
Zingam merged commit 9109452 into llvm:main Sep 19, 2026
83 checks passed
@LucasChollet
LucasChollet deleted the libcxx-atomic-wakeup branch September 19, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[libc++] lost_wakeup.pass.cpp test case for atomic_wait is flaky

4 participants