1 // Copyright 2023 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/task/thread_pool/semaphore.h"
6
7 #include <memory>
8 #include <vector>
9
10 #include "base/functional/callback.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/synchronization/lock.h"
13 #include "base/test/bind.h"
14 #include "base/test/test_timeouts.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/threading/thread.h"
17 #include "base/time/time.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "testing/platform_test.h"
20
21 namespace base {
22
23 namespace {
24
25 class SemaphoreTest : public PlatformTest {
26 protected:
CreateThreadWithTask(RepeatingClosure & thread_task)27 raw_ptr<Thread> CreateThreadWithTask(RepeatingClosure& thread_task) {
28 std::unique_ptr<Thread> thread = std::make_unique<Thread>(
29 StringPrintf("SemTestThread%d", threadcounter++));
30
31 thread->Start();
32 thread->task_runner()->PostTask(FROM_HERE, thread_task);
33 threads_.push_back(std::move(thread));
34 return threads_.back().get();
35 }
36
37 int threadcounter = 0;
38 WaitableEvent shutdown_event_{};
39 std::vector<std::unique_ptr<Thread>> threads_{};
40 };
41
42 } // namespace
43
TEST_F(SemaphoreTest,BasicWait)44 TEST_F(SemaphoreTest, BasicWait) {
45 internal::Semaphore sem{0};
46 RepeatingClosure task = BindLambdaForTesting([&]() {
47 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
48 sem.Signal();
49 });
50 const auto start_time = TimeTicks::Now();
51 CreateThreadWithTask(task);
52
53 sem.Wait();
54 EXPECT_GE(TimeTicks::Now() - start_time, TestTimeouts::tiny_timeout());
55 }
56
57 // TimedWait(TimeDelta::Max()) should be equivalent to Wait().
TEST_F(SemaphoreTest,MaxTimedWait)58 TEST_F(SemaphoreTest, MaxTimedWait) {
59 internal::Semaphore sem{0};
60 RepeatingClosure task = BindLambdaForTesting([&]() {
61 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
62 sem.Signal();
63 });
64 const auto start_time = TimeTicks::Now();
65 CreateThreadWithTask(task);
66
67 EXPECT_TRUE(sem.TimedWait(TimeDelta::Max()));
68 EXPECT_GE(TimeTicks::Now() - start_time, TestTimeouts::tiny_timeout());
69 }
70
TEST_F(SemaphoreTest,TimedWaitFail)71 TEST_F(SemaphoreTest, TimedWaitFail) {
72 internal::Semaphore sem{0};
73 RepeatingClosure task = BindLambdaForTesting([&]() {
74 TimeTicks start_time = TimeTicks::Now();
75 EXPECT_FALSE(sem.TimedWait(TestTimeouts::tiny_timeout()));
76 EXPECT_GE(TimeTicks::Now() - start_time, TestTimeouts::tiny_timeout());
77 });
78
79 CreateThreadWithTask(task)->FlushForTesting();
80 }
81
TEST_F(SemaphoreTest,TimedWaitSuccess)82 TEST_F(SemaphoreTest, TimedWaitSuccess) {
83 internal::Semaphore sem{0};
84 RepeatingClosure task = BindLambdaForTesting(
85 [&]() { EXPECT_TRUE(sem.TimedWait(TestTimeouts::tiny_timeout())); });
86
87 sem.Signal();
88 CreateThreadWithTask(task)->FlushForTesting();
89 }
90
TEST_F(SemaphoreTest,PingPongCounter)91 TEST_F(SemaphoreTest, PingPongCounter) {
92 internal::Semaphore sem{0};
93 int counter = 0;
94 RepeatingClosure task = BindLambdaForTesting([&]() {
95 while (!shutdown_event_.IsSignaled()) {
96 sem.Wait();
97 {
98 if (shutdown_event_.IsSignaled()) {
99 return;
100 }
101 }
102 ++counter;
103 if (counter > 999) {
104 shutdown_event_.Signal();
105 }
106 sem.Signal();
107 PlatformThread::Sleep(Microseconds(100));
108 }
109 });
110
111 sem.Signal();
112 raw_ptr<Thread> thread = CreateThreadWithTask(task);
113 raw_ptr<Thread> thread2 = CreateThreadWithTask(task);
114 thread->FlushForTesting();
115 thread2->FlushForTesting();
116 thread->Stop();
117 thread2->Stop();
118 EXPECT_EQ(counter, 1000);
119 }
120
121 } // namespace base
122