1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/synchronization/cancelable_event.h"
6
7 #include <stdint.h>
8
9 #include <atomic>
10 #include <memory>
11 #include <tuple>
12 #include <vector>
13
14 #include "base/barrier_closure.h"
15 #include "base/functional/callback.h"
16 #include "base/logging.h"
17 #include "base/numerics/clamped_math.h"
18 #include "base/rand_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/synchronization/lock.h"
21 #include "base/test/bind.h"
22 #include "base/test/test_timeouts.h"
23 #include "base/test/test_waitable_event.h"
24 #include "base/threading/thread.h"
25 #include "base/time/time.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "testing/platform_test.h"
28
29 namespace base {
30
31 namespace {
32
33 class CancelableEventTest : public testing::Test {
34 protected:
CreateThreadWithTask(RepeatingClosure & thread_task)35 raw_ptr<Thread> CreateThreadWithTask(RepeatingClosure& thread_task) {
36 std::unique_ptr<Thread> thread = std::make_unique<Thread>(
37 StringPrintf("CancelTestThread%d", threadcounter++));
38
39 thread->Start();
40 thread->task_runner()->PostTask(FROM_HERE, thread_task);
41 threads_.push_back(std::move(thread));
42 return threads_.back().get();
43 }
44
45 int threadcounter = 0;
46 WaitableEvent shutdown_event_;
47 std::vector<std::unique_ptr<Thread>> threads_;
48 };
49
50 } // namespace
51
TEST_F(CancelableEventTest,TimedWaitFail)52 TEST_F(CancelableEventTest, TimedWaitFail) {
53 CancelableEvent event;
54 RepeatingClosure task = BindLambdaForTesting([&]() {
55 TimeTicks start_time = TimeTicks::Now();
56 EXPECT_FALSE(event.TimedWait(TestTimeouts::tiny_timeout()));
57 EXPECT_GE(TimeTicks::Now() - start_time, TestTimeouts::tiny_timeout());
58 });
59
60 this->CreateThreadWithTask(task)->FlushForTesting();
61 }
62
TEST_F(CancelableEventTest,TimedWaitSuccess)63 TEST_F(CancelableEventTest, TimedWaitSuccess) {
64 CancelableEvent event;
65 RepeatingClosure task = BindLambdaForTesting(
66 [&]() { EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); });
67
68 event.Signal();
69 this->CreateThreadWithTask(task)->FlushForTesting();
70 }
71
72 // These are the platforms on which a functional CancelableEvent is implemented.
73 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || \
74 BUILDFLAG(IS_ANDROID)
75
TEST_F(CancelableEventTest,CancelSucceedsWhenNoWaiterAndWaitTimesOut)76 TEST_F(CancelableEventTest, CancelSucceedsWhenNoWaiterAndWaitTimesOut) {
77 CancelableEvent event;
78 event.Signal();
79 EXPECT_TRUE(event.Cancel());
80 EXPECT_FALSE(event.TimedWait(base::TimeDelta()));
81 }
82
TEST_F(CancelableEventTest,BothCancelFailureAndSucceedOccurWithOneWaiter)83 TEST_F(CancelableEventTest, BothCancelFailureAndSucceedOccurWithOneWaiter) {
84 bool cancel_failed = false;
85 bool cancel_succeeded = false;
86 for (int i = 0; i < 100; ++i) {
87 CancelableEvent event;
88 TestWaitableEvent thread_running;
89 auto task = BindLambdaForTesting([&]() {
90 thread_running.Signal();
91 event.Wait();
92 });
93 auto thread = CreateThreadWithTask(task);
94 if (!cancel_failed) {
95 thread_running.Wait();
96 }
97 event.Signal();
98 #if BUILDFLAG(IS_POSIX)
99 // Posix implementations of Semaphores seem to be much less greedy in waking
100 // up threads currently waiting on the event - give the thread a few
101 // milliseconds to wake up and acquire the semaphore before us.
102 if (cancel_succeeded) {
103 PlatformThread::Sleep(Milliseconds(8));
104 }
105 #endif
106 if (event.Cancel()) {
107 cancel_succeeded = true;
108 event.Signal();
109 } else {
110 cancel_failed = true;
111 }
112 thread->FlushForTesting();
113 if (cancel_failed && cancel_succeeded) {
114 break;
115 }
116 }
117 EXPECT_TRUE(cancel_failed);
118 EXPECT_TRUE(cancel_succeeded);
119 }
120
TEST_F(CancelableEventTest,BothCancelFailureAndSucceedOccurUnderContention)121 TEST_F(CancelableEventTest, BothCancelFailureAndSucceedOccurUnderContention) {
122 // The following block is responsible for creating CPU contention - it creates
123 // 16 threads which run for the duration of the test, crunching CPU. None of
124 // the data here is used in any meaningful way in the rest of the test. beyond
125 // setting `busywork_threads_should_quit` to signal exit.
126 std::atomic_bool busywork_threads_should_quit = false;
127 // The arena lives for the duration of the test, and so must have test-wide
128 // scope, however it is only accessed by the busywork threads, and not by the
129 // rest of the test.
130 const int kNumThreads = 16;
131 std::atomic_char busywork_arena[kNumThreads];
132 {
133 TestWaitableEvent threads_running;
134 RepeatingClosure threads_running_barrier = BarrierClosure(
135 kNumThreads,
136 BindOnce(&TestWaitableEvent::Signal, Unretained(&threads_running)));
137 for (int i = 0; i < kNumThreads; ++i) {
138 auto task = BindLambdaForTesting([&]() {
139 threads_running_barrier.Run();
140 while (!busywork_threads_should_quit.load(std::memory_order_acquire)) {
141 // Busy loop. Only done to burn CPU.
142 uint64_t counter = 1;
143 for (auto& slot : busywork_arena) {
144 counter += slot + 1;
145 slot.store(static_cast<char>((counter & 0xf) + 1),
146 std::memory_order_release);
147 }
148 }
149 });
150 CreateThreadWithTask(task);
151 }
152 threads_running.Wait();
153 }
154
155 // Used to adjust race timings to favor the case which has not yet been seen
156 // (cancel fail/success).
157 internal::ClampedNumeric<unsigned int> wait_ms = 0;
158 bool succeeded = false;
159 bool failed = false;
160 for (int i = 0; i < 10 && (!failed || !succeeded); ++i) {
161 CancelableEvent event;
162 TestWaitableEvent thread_running;
163 std::atomic_bool thread_done = false;
164 auto task = BindLambdaForTesting([&]() {
165 thread_running.Signal();
166 EXPECT_TRUE(event.TimedWait(TestTimeouts::test_launcher_timeout()));
167 thread_done = true;
168 });
169 auto thread = CreateThreadWithTask(task);
170 PlatformThread::Sleep(Milliseconds(wait_ms));
171 event.Signal();
172 PlatformThread::Sleep(Milliseconds(wait_ms));
173 if (event.Cancel()) {
174 succeeded = true;
175 event.Signal();
176 wait_ms += 100;
177 } else {
178 failed = true;
179 wait_ms -= 50;
180 }
181 thread->FlushForTesting();
182 EXPECT_TRUE(thread_done);
183 }
184 busywork_threads_should_quit.store(true, std::memory_order_release);
185 for (auto& thread : threads_) {
186 thread->FlushForTesting();
187 }
188 EXPECT_TRUE(succeeded);
189 EXPECT_TRUE(failed);
190 }
191
192 #else
193
TEST_F(CancelableEventTest,CancelFailsOnUnsupportedPlatforms)194 TEST_F(CancelableEventTest, CancelFailsOnUnsupportedPlatforms) {
195 CancelableEvent event;
196 event.Signal();
197 EXPECT_FALSE(event.Cancel());
198 EXPECT_TRUE(event.TimedWait(base::TimeDelta()));
199 }
200
201 #endif
202
203 } // namespace base
204