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/thread.h"
16 #include "base/time/time.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "testing/platform_test.h"
19
20 namespace base {
21
22 namespace {
23
24 class SemaphoreTest : public PlatformTest {
25 protected:
CreateThreadWithTask(RepeatingClosure & thread_task)26 raw_ptr<Thread> CreateThreadWithTask(RepeatingClosure& thread_task) {
27 std::unique_ptr<Thread> thread = std::make_unique<Thread>(
28 StringPrintf("SemTestThread%d", threadcounter++));
29
30 thread->Start();
31 thread->task_runner()->PostTask(FROM_HERE, thread_task);
32 threads_.push_back(std::move(thread));
33 return threads_.back().get();
34 }
35
36 int threadcounter = 0;
37 WaitableEvent shutdown_event_{};
38 std::vector<std::unique_ptr<Thread>> threads_{};
39 };
40
41 } // namespace
42
TEST_F(SemaphoreTest,TimedWaitFail)43 TEST_F(SemaphoreTest, TimedWaitFail) {
44 internal::Semaphore sem{0};
45 RepeatingClosure task = BindLambdaForTesting([&]() {
46 TimeTicks start_time = TimeTicks::Now();
47 EXPECT_FALSE(sem.TimedWait(TestTimeouts::tiny_timeout()));
48 EXPECT_GE(TimeTicks::Now() - start_time, TestTimeouts::tiny_timeout());
49 });
50
51 CreateThreadWithTask(task)->FlushForTesting();
52 }
53
TEST_F(SemaphoreTest,TimedWaitSuccess)54 TEST_F(SemaphoreTest, TimedWaitSuccess) {
55 internal::Semaphore sem{0};
56 RepeatingClosure task = BindLambdaForTesting(
57 [&]() { EXPECT_TRUE(sem.TimedWait(TestTimeouts::tiny_timeout())); });
58
59 sem.Signal();
60 CreateThreadWithTask(task)->FlushForTesting();
61 }
62
TEST_F(SemaphoreTest,PingPongCounter)63 TEST_F(SemaphoreTest, PingPongCounter) {
64 internal::Semaphore sem{0};
65 int counter = 0;
66 RepeatingClosure task = BindLambdaForTesting([&]() {
67 while (!shutdown_event_.IsSignaled()) {
68 sem.Wait();
69 {
70 if (shutdown_event_.IsSignaled()) {
71 return;
72 }
73 }
74 ++counter;
75 if (counter > 999) {
76 shutdown_event_.Signal();
77 }
78 sem.Signal();
79 PlatformThread::Sleep(Microseconds(100));
80 }
81 });
82
83 sem.Signal();
84 raw_ptr<Thread> thread = CreateThreadWithTask(task);
85 raw_ptr<Thread> thread2 = CreateThreadWithTask(task);
86 thread->FlushForTesting();
87 thread2->FlushForTesting();
88 thread->Stop();
89 thread2->Stop();
90 EXPECT_EQ(counter, 1000);
91 }
92
93 } // namespace base
94