• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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