xref: /aosp_15_r20/external/federated-compute/fcp/base/scheduler_test.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
1*14675a02SAndroid Build Coastguard Worker /*
2*14675a02SAndroid Build Coastguard Worker  * Copyright 2018 Google LLC
3*14675a02SAndroid Build Coastguard Worker  *
4*14675a02SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*14675a02SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*14675a02SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*14675a02SAndroid Build Coastguard Worker  *
8*14675a02SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*14675a02SAndroid Build Coastguard Worker  *
10*14675a02SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*14675a02SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*14675a02SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*14675a02SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*14675a02SAndroid Build Coastguard Worker  * limitations under the License.
15*14675a02SAndroid Build Coastguard Worker  */
16*14675a02SAndroid Build Coastguard Worker 
17*14675a02SAndroid Build Coastguard Worker #include "fcp/base/scheduler.h"
18*14675a02SAndroid Build Coastguard Worker 
19*14675a02SAndroid Build Coastguard Worker #include <atomic>
20*14675a02SAndroid Build Coastguard Worker #include <cstdlib>  // for std::rand
21*14675a02SAndroid Build Coastguard Worker 
22*14675a02SAndroid Build Coastguard Worker #include "gmock/gmock.h"
23*14675a02SAndroid Build Coastguard Worker #include "gtest/gtest.h"
24*14675a02SAndroid Build Coastguard Worker #include "absl/synchronization/blocking_counter.h"
25*14675a02SAndroid Build Coastguard Worker #include "fcp/base/monitoring.h"
26*14675a02SAndroid Build Coastguard Worker #include "fcp/testing/testing.h"
27*14675a02SAndroid Build Coastguard Worker 
28*14675a02SAndroid Build Coastguard Worker namespace fcp {
29*14675a02SAndroid Build Coastguard Worker namespace base {
30*14675a02SAndroid Build Coastguard Worker namespace {
31*14675a02SAndroid Build Coastguard Worker 
32*14675a02SAndroid Build Coastguard Worker // NOTE: many of tests below use reference captures in lambdas for locals.
33*14675a02SAndroid Build Coastguard Worker // This is sound because the test methods do not return before the thread
34*14675a02SAndroid Build Coastguard Worker // pool has become idle (pool->WaitUntilIdle()).
35*14675a02SAndroid Build Coastguard Worker 
36*14675a02SAndroid Build Coastguard Worker // Tests whether scheduled tasks are successfully executed.
TEST(ThreadPool,TasksAreExecuted)37*14675a02SAndroid Build Coastguard Worker TEST(ThreadPool, TasksAreExecuted) {
38*14675a02SAndroid Build Coastguard Worker   auto pool = CreateThreadPoolScheduler(2);
39*14675a02SAndroid Build Coastguard Worker 
40*14675a02SAndroid Build Coastguard Worker   bool b1 = false;
41*14675a02SAndroid Build Coastguard Worker   bool b2 = false;
42*14675a02SAndroid Build Coastguard Worker   pool->Schedule([&b1]() { b1 = true; });
43*14675a02SAndroid Build Coastguard Worker   pool->Schedule([&b2]() { b2 = true; });
44*14675a02SAndroid Build Coastguard Worker 
45*14675a02SAndroid Build Coastguard Worker   pool->WaitUntilIdle();
46*14675a02SAndroid Build Coastguard Worker 
47*14675a02SAndroid Build Coastguard Worker   EXPECT_TRUE(b1);
48*14675a02SAndroid Build Coastguard Worker   EXPECT_TRUE(b2);
49*14675a02SAndroid Build Coastguard Worker }
50*14675a02SAndroid Build Coastguard Worker 
51*14675a02SAndroid Build Coastguard Worker // Tests whether the pool uses actually multiple threads to execute tasks.
52*14675a02SAndroid Build Coastguard Worker // The test goal is achieved by blocking in one task until another task
53*14675a02SAndroid Build Coastguard Worker // unblocks, which can only work if multiple threads are used.
TEST(ThreadPool,ThreadsAreUtilized)54*14675a02SAndroid Build Coastguard Worker TEST(ThreadPool, ThreadsAreUtilized) {
55*14675a02SAndroid Build Coastguard Worker   auto pool = CreateThreadPoolScheduler(2);
56*14675a02SAndroid Build Coastguard Worker 
57*14675a02SAndroid Build Coastguard Worker   absl::BlockingCounter counter(1);
58*14675a02SAndroid Build Coastguard Worker   bool b1 = false;
59*14675a02SAndroid Build Coastguard Worker   bool b2 = false;
60*14675a02SAndroid Build Coastguard Worker 
61*14675a02SAndroid Build Coastguard Worker   pool->Schedule([&b1, &counter] {
62*14675a02SAndroid Build Coastguard Worker     counter.Wait();
63*14675a02SAndroid Build Coastguard Worker     b1 = true;
64*14675a02SAndroid Build Coastguard Worker   });
65*14675a02SAndroid Build Coastguard Worker   pool->Schedule([&b2, &counter] {
66*14675a02SAndroid Build Coastguard Worker     counter.DecrementCount();
67*14675a02SAndroid Build Coastguard Worker     b2 = true;
68*14675a02SAndroid Build Coastguard Worker   });
69*14675a02SAndroid Build Coastguard Worker 
70*14675a02SAndroid Build Coastguard Worker   pool->WaitUntilIdle();
71*14675a02SAndroid Build Coastguard Worker 
72*14675a02SAndroid Build Coastguard Worker   EXPECT_TRUE(b1);
73*14675a02SAndroid Build Coastguard Worker   EXPECT_TRUE(b2);
74*14675a02SAndroid Build Coastguard Worker }
75*14675a02SAndroid Build Coastguard Worker 
TEST(ThreadPool,StressTest)76*14675a02SAndroid Build Coastguard Worker TEST(ThreadPool, StressTest) {
77*14675a02SAndroid Build Coastguard Worker   // A simple stress test where we spawn many threads and let them after
78*14675a02SAndroid Build Coastguard Worker   // a random wait time increment a counter.
79*14675a02SAndroid Build Coastguard Worker   static constexpr int kThreads = 32;
80*14675a02SAndroid Build Coastguard Worker   static constexpr int kIterations = 16;
81*14675a02SAndroid Build Coastguard Worker   auto pool = CreateThreadPoolScheduler(kThreads);
82*14675a02SAndroid Build Coastguard Worker   std::atomic<int64_t> atomic_counter{0};
83*14675a02SAndroid Build Coastguard Worker 
84*14675a02SAndroid Build Coastguard Worker   for (auto i = 0; i < kThreads; ++i) {
85*14675a02SAndroid Build Coastguard Worker     auto task = [&atomic_counter] {
86*14675a02SAndroid Build Coastguard Worker       for (auto j = 0; j < kIterations; ++j) {
87*14675a02SAndroid Build Coastguard Worker         absl::SleepFor(absl::Microseconds(std::rand() % 500));
88*14675a02SAndroid Build Coastguard Worker         atomic_counter.fetch_add(1);
89*14675a02SAndroid Build Coastguard Worker       }
90*14675a02SAndroid Build Coastguard Worker     };
91*14675a02SAndroid Build Coastguard Worker     pool->Schedule(task);
92*14675a02SAndroid Build Coastguard Worker   }
93*14675a02SAndroid Build Coastguard Worker 
94*14675a02SAndroid Build Coastguard Worker   pool->WaitUntilIdle();
95*14675a02SAndroid Build Coastguard Worker   ASSERT_EQ(atomic_counter, kThreads * kIterations);
96*14675a02SAndroid Build Coastguard Worker }
97*14675a02SAndroid Build Coastguard Worker 
TEST(Worker,TasksAreExecutedSequentially)98*14675a02SAndroid Build Coastguard Worker TEST(Worker, TasksAreExecutedSequentially) {
99*14675a02SAndroid Build Coastguard Worker   auto pool = CreateThreadPoolScheduler(3);
100*14675a02SAndroid Build Coastguard Worker   auto worker = pool->CreateWorker();
101*14675a02SAndroid Build Coastguard Worker   absl::Mutex mutex{};
102*14675a02SAndroid Build Coastguard Worker   std::vector<int> recorded{};
103*14675a02SAndroid Build Coastguard Worker   for (int i = 0; i < 128; i++) {
104*14675a02SAndroid Build Coastguard Worker     worker->Schedule([&mutex, &recorded, i] {
105*14675a02SAndroid Build Coastguard Worker       // Expect that no one is holding the mutex (tests for non-overlap).
106*14675a02SAndroid Build Coastguard Worker       if (mutex.TryLock()) {
107*14675a02SAndroid Build Coastguard Worker         // Add i to the recorded values (tests for execution in order).
108*14675a02SAndroid Build Coastguard Worker         recorded.push_back(i);
109*14675a02SAndroid Build Coastguard Worker         // Idle wait to be sure we don't execute faster than we schedule
110*14675a02SAndroid Build Coastguard Worker         absl::SleepFor(absl::Milliseconds(50));
111*14675a02SAndroid Build Coastguard Worker         mutex.Unlock();
112*14675a02SAndroid Build Coastguard Worker       } else {
113*14675a02SAndroid Build Coastguard Worker         FAIL() << "mutex was unexpectedly hold";
114*14675a02SAndroid Build Coastguard Worker       }
115*14675a02SAndroid Build Coastguard Worker     });
116*14675a02SAndroid Build Coastguard Worker   }
117*14675a02SAndroid Build Coastguard Worker   pool->WaitUntilIdle();
118*14675a02SAndroid Build Coastguard Worker 
119*14675a02SAndroid Build Coastguard Worker   // Verify recorded values.
120*14675a02SAndroid Build Coastguard Worker   for (int i = 0; i < 128; i++) {
121*14675a02SAndroid Build Coastguard Worker     ASSERT_EQ(recorded[i], i);
122*14675a02SAndroid Build Coastguard Worker   }
123*14675a02SAndroid Build Coastguard Worker }
124*14675a02SAndroid Build Coastguard Worker 
125*14675a02SAndroid Build Coastguard Worker }  // namespace
126*14675a02SAndroid Build Coastguard Worker 
127*14675a02SAndroid Build Coastguard Worker }  // namespace base
128*14675a02SAndroid Build Coastguard Worker }  // namespace fcp
129