xref: /aosp_15_r20/external/cronet/base/task/common/operations_controller_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2018 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/common/operations_controller.h"
6 
7 #include <atomic>
8 #include <cstdint>
9 #include <utility>
10 
11 #include "base/memory/raw_ref.h"
12 #include "base/ranges/algorithm.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/threading/simple_thread.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace base {
18 namespace internal {
19 namespace {
20 
21 class ScopedShutdown {
22  public:
ScopedShutdown(OperationsController * controller)23   ScopedShutdown(OperationsController* controller) : controller_(*controller) {}
~ScopedShutdown()24   ~ScopedShutdown() { controller_->ShutdownAndWaitForZeroOperations(); }
25 
26  private:
27   const raw_ref<OperationsController> controller_;
28 };
29 
TEST(OperationsControllerTest,CanBeDestroyedWithoutWaiting)30 TEST(OperationsControllerTest, CanBeDestroyedWithoutWaiting) {
31   OperationsController controller;
32 }
33 
TEST(OperationsControllerTest,CanShutdownIfNotStarted)34 TEST(OperationsControllerTest, CanShutdownIfNotStarted) {
35   OperationsController controller;
36 
37   controller.ShutdownAndWaitForZeroOperations();
38 }
39 
TEST(OperationsControllerTest,FailsToBeginWhenNotStarted)40 TEST(OperationsControllerTest, FailsToBeginWhenNotStarted) {
41   OperationsController controller;
42 
43   auto operation_token = controller.TryBeginOperation();
44 
45   EXPECT_FALSE(operation_token);
46 }
47 
TEST(OperationsControllerTest,CanShutdownAfterTryCallsIfNotStarted)48 TEST(OperationsControllerTest, CanShutdownAfterTryCallsIfNotStarted) {
49   OperationsController controller;
50   auto operation_token = controller.TryBeginOperation();
51   ASSERT_FALSE(operation_token);
52 
53   controller.ShutdownAndWaitForZeroOperations();
54 }
55 
TEST(OperationsControllerTest,StartAcceptingOperationsReturnsFalseIfNoRejectedBeginAttempts)56 TEST(OperationsControllerTest,
57      StartAcceptingOperationsReturnsFalseIfNoRejectedBeginAttempts) {
58   OperationsController controller;
59   ScopedShutdown cleanup(&controller);
60 
61   EXPECT_FALSE(controller.StartAcceptingOperations());
62 }
63 
TEST(OperationsControllerTest,StartAcceptingOperationsReturnsTrueIfFailedBeginAttempts)64 TEST(OperationsControllerTest,
65      StartAcceptingOperationsReturnsTrueIfFailedBeginAttempts) {
66   OperationsController controller;
67   ScopedShutdown cleanup(&controller);
68 
69   auto operation_token = controller.TryBeginOperation();
70   ASSERT_FALSE(operation_token);
71 
72   EXPECT_TRUE(controller.StartAcceptingOperations());
73 }
74 
TEST(OperationsControllerTest,SuccesfulBeginReturnsValidScopedObject)75 TEST(OperationsControllerTest, SuccesfulBeginReturnsValidScopedObject) {
76   OperationsController controller;
77   ScopedShutdown cleanup(&controller);
78   controller.StartAcceptingOperations();
79 
80   auto operation_token = controller.TryBeginOperation();
81 
82   EXPECT_TRUE(operation_token);
83 }
84 
TEST(OperationsControllerTest,BeginFailsAfterShutdown)85 TEST(OperationsControllerTest, BeginFailsAfterShutdown) {
86   OperationsController controller;
87   controller.StartAcceptingOperations();
88 
89   controller.ShutdownAndWaitForZeroOperations();
90   auto operation_token = controller.TryBeginOperation();
91 
92   EXPECT_FALSE(operation_token);
93 }
94 
TEST(OperationsControllerTest,ScopedOperationsControllerIsMoveConstructible)95 TEST(OperationsControllerTest, ScopedOperationsControllerIsMoveConstructible) {
96   OperationsController controller;
97   ScopedShutdown cleanup(&controller);
98 
99   controller.StartAcceptingOperations();
100   auto operation_token_1 = controller.TryBeginOperation();
101   auto operation_token_2 = std::move(operation_token_1);
102 
103   EXPECT_FALSE(operation_token_1);
104   EXPECT_TRUE(operation_token_2);
105 }
106 
107 // Dummy SimpleThread implementation that periodically begins and ends
108 // operations until one of them fails.
109 class TestThread : public SimpleThread {
110  public:
TestThread(OperationsController * ref_controller,std::atomic<bool> * started,std::atomic<int32_t> * thread_counter)111   explicit TestThread(OperationsController* ref_controller,
112                       std::atomic<bool>* started,
113                       std::atomic<int32_t>* thread_counter)
114       : SimpleThread("TestThread"),
115         controller_(*ref_controller),
116         started_(*started),
117         thread_counter_(*thread_counter) {}
Run()118   void Run() override {
119     thread_counter_->fetch_add(1, std::memory_order_relaxed);
120     while (true) {
121       PlatformThread::YieldCurrentThread();
122       bool was_started = started_->load(std::memory_order_relaxed);
123       std::vector<OperationsController::OperationToken> tokens;
124       for (int i = 0; i < 100; ++i) {
125         tokens.push_back(controller_->TryBeginOperation());
126       }
127       if (!was_started)
128         continue;
129       if (ranges::any_of(tokens, [](const auto& token) { return !token; })) {
130         break;
131       }
132     }
133   }
134 
135  private:
136   const raw_ref<OperationsController> controller_;
137   const raw_ref<std::atomic<bool>> started_;
138   const raw_ref<std::atomic<int32_t>> thread_counter_;
139 };
140 
TEST(OperationsControllerTest,BeginsFromMultipleThreads)141 TEST(OperationsControllerTest, BeginsFromMultipleThreads) {
142   constexpr int32_t kNumThreads = 10;
143   for (int32_t i = 0; i < 10; ++i) {
144     OperationsController ref_controller;
145     std::atomic<bool> started(false);
146     std::atomic<int32_t> running_threads(0);
147     std::vector<std::unique_ptr<TestThread>> threads;
148     for (int j = 0; j < kNumThreads; ++j) {
149       threads.push_back(std::make_unique<TestThread>(&ref_controller, &started,
150                                                      &running_threads));
151       threads.back()->Start();
152     }
153 
154     // Make sure all threads are running.
155     while (running_threads.load(std::memory_order_relaxed) != kNumThreads) {
156       PlatformThread::YieldCurrentThread();
157     }
158 
159     // Wait a bit before starting to try to introduce races.
160     constexpr TimeDelta kRaceInducingTimeout = Microseconds(50);
161     PlatformThread::Sleep(kRaceInducingTimeout);
162 
163     ref_controller.StartAcceptingOperations();
164     // Signal threads to terminate on TryBeginOperation() failures
165     started.store(true, std::memory_order_relaxed);
166 
167     // Let the test run for a while before shuting down.
168     PlatformThread::Sleep(Milliseconds(5));
169     ref_controller.ShutdownAndWaitForZeroOperations();
170     for (const auto& t : threads) {
171       t->Join();
172     }
173   }
174 }
175 
176 }  // namespace
177 }  // namespace internal
178 }  // namespace base
179