1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <algorithm>
18 #include <chrono>
19 #include <future>
20 #include <thread>
21
22 #include <gtest/gtest.h>
23
24 #include "thread/WorkerThread.h"
25
26 namespace {
27
28 using namespace aidl::android::hardware::biometrics;
29 using namespace std::chrono_literals;
30
TEST(WorkerThreadTest,ScheduleReturnsTrueWhenQueueHasSpace)31 TEST(WorkerThreadTest, ScheduleReturnsTrueWhenQueueHasSpace) {
32 WorkerThread worker(1 /*maxQueueSize*/);
33 for (int i = 0; i < 100; ++i) {
34 std::promise<void> promise;
35 auto future = promise.get_future();
36
37 ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise)]() mutable {
38 // Notify that the task has started.
39 promise.set_value();
40 })));
41
42 future.wait();
43 }
44 }
45
TEST(WorkerThreadTest,ScheduleReturnsFalseWhenQueueIsFull)46 TEST(WorkerThreadTest, ScheduleReturnsFalseWhenQueueIsFull) {
47 WorkerThread worker(2 /*maxQueueSize*/);
48
49 std::promise<void> promise;
50 auto future = promise.get_future();
51
52 // Schedule a long-running task.
53 ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise)]() mutable {
54 // Notify that the task has started.
55 promise.set_value();
56 // Block for a "very long" time.
57 std::this_thread::sleep_for(1s);
58 })));
59
60 // Make sure the long-running task began executing.
61 future.wait();
62
63 // The first task is already being worked on, which means the queue must be empty.
64 // Fill the worker's queue to the maximum.
65 ASSERT_TRUE(worker.schedule(Callable::from([] {})));
66 ASSERT_TRUE(worker.schedule(Callable::from([] {})));
67
68 EXPECT_FALSE(worker.schedule(Callable::from([] {})));
69 }
70
TEST(WorkerThreadTest,TasksExecuteInOrder)71 TEST(WorkerThreadTest, TasksExecuteInOrder) {
72 constexpr int NUM_TASKS = 10000;
73 WorkerThread worker(NUM_TASKS + 1);
74
75 std::mutex mut;
76 std::condition_variable cv;
77 bool finished = false;
78 std::vector<int> results;
79
80 for (int i = 0; i < NUM_TASKS; ++i) {
81 worker.schedule(Callable::from([&mut, &results, i] {
82 // Delay tasks differently to provoke races.
83 std::this_thread::sleep_for(std::chrono::nanoseconds(100 - i % 100));
84 auto lock = std::lock_guard(mut);
85 results.push_back(i);
86 }));
87 }
88
89 // Schedule a special task to signal when all of the tasks are finished.
90 worker.schedule(Callable::from([&mut, &cv, &finished] {
91 auto lock = std::lock_guard(mut);
92 finished = true;
93 cv.notify_one();
94 }));
95
96 auto lock = std::unique_lock(mut);
97 cv.wait(lock, [&finished] { return finished; });
98 ASSERT_EQ(results.size(), NUM_TASKS);
99 EXPECT_TRUE(std::is_sorted(results.begin(), results.end()));
100 }
101
TEST(WorkerThreadTest,ExecutionStopsAfterWorkerIsDestroyed)102 TEST(WorkerThreadTest, ExecutionStopsAfterWorkerIsDestroyed) {
103 std::promise<void> promise1;
104 std::promise<void> promise2;
105 auto future1 = promise1.get_future();
106 auto future2 = promise2.get_future();
107 std::atomic<bool> value;
108
109 // Local scope for the worker to test its destructor when it goes out of scope.
110 {
111 WorkerThread worker(2 /*maxQueueSize*/);
112
113 ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise1)]() mutable {
114 promise.set_value();
115 std::this_thread::sleep_for(200ms);
116 })));
117
118 // The first task should start executing.
119 future1.wait();
120
121 // The second task should schedule successfully.
122 ASSERT_TRUE(
123 worker.schedule(Callable::from([promise = std::move(promise2), &value]() mutable {
124 // The worker should destruct before it gets a chance to execute this.
125 value = true;
126 promise.set_value();
127 })));
128 }
129
130 // The second task should never execute.
131 future2.wait();
132 // The future is expected to be ready but contain an exception.
133 // Cannot use ASSERT_THROW because exceptions are disabled in this codebase.
134 // ASSERT_THROW(future2.get(), std::future_error);
135 EXPECT_FALSE(value);
136 }
137
138 } // namespace
139