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 "WorkerThread.h"
25
26 namespace {
27
28 using aidl::android::hardware::biometrics::fingerprint::Callable;
29 using aidl::android::hardware::biometrics::fingerprint::WorkerThread;
30 using namespace std::chrono_literals;
31
TEST(WorkerThreadTest,ScheduleReturnsTrueWhenQueueHasSpace)32 TEST(WorkerThreadTest, ScheduleReturnsTrueWhenQueueHasSpace) {
33 WorkerThread worker(1 /*maxQueueSize*/);
34 for (int i = 0; i < 100; ++i) {
35 std::promise<void> promise;
36 auto future = promise.get_future();
37
38 ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise)]() mutable {
39 // Notify that the task has started.
40 promise.set_value();
41 })));
42
43 future.wait();
44 }
45 }
46
TEST(WorkerThreadTest,ScheduleReturnsFalseWhenQueueIsFull)47 TEST(WorkerThreadTest, ScheduleReturnsFalseWhenQueueIsFull) {
48 WorkerThread worker(2 /*maxQueueSize*/);
49
50 std::promise<void> promise;
51 auto future = promise.get_future();
52
53 // Schedule a long-running task.
54 ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise)]() mutable {
55 // Notify that the task has started.
56 promise.set_value();
57 // Block for a "very long" time.
58 std::this_thread::sleep_for(1s);
59 })));
60
61 // Make sure the long-running task began executing.
62 future.wait();
63
64 // The first task is already being worked on, which means the queue must be empty.
65 // Fill the worker's queue to the maximum.
66 ASSERT_TRUE(worker.schedule(Callable::from([] {})));
67 ASSERT_TRUE(worker.schedule(Callable::from([] {})));
68
69 EXPECT_FALSE(worker.schedule(Callable::from([] {})));
70 }
71
TEST(WorkerThreadTest,TasksExecuteInOrder)72 TEST(WorkerThreadTest, TasksExecuteInOrder) {
73 constexpr int NUM_TASKS = 10000;
74 WorkerThread worker(NUM_TASKS);
75
76 std::mutex mut;
77 std::condition_variable cv;
78 bool finished = false;
79 std::vector<int> results;
80
81 for (int i = 0; i < NUM_TASKS; ++i) {
82 worker.schedule(Callable::from([&mut, &results, i] {
83 // Delay tasks differently to provoke races.
84 std::this_thread::sleep_for(std::chrono::nanoseconds(100 - i % 100));
85 auto lock = std::lock_guard(mut);
86 results.push_back(i);
87 }));
88 }
89
90 // Schedule a special task to signal when all of the tasks are finished.
91 worker.schedule(Callable::from([&mut, &cv, &finished] {
92 auto lock = std::lock_guard(mut);
93 finished = true;
94 cv.notify_one();
95 }));
96
97 auto lock = std::unique_lock(mut);
98 cv.wait(lock, [&finished] { return finished; });
99 ASSERT_EQ(results.size(), NUM_TASKS);
100 EXPECT_TRUE(std::is_sorted(results.begin(), results.end()));
101 }
102
TEST(WorkerThreadTest,ExecutionStopsAfterWorkerIsDestroyed)103 TEST(WorkerThreadTest, ExecutionStopsAfterWorkerIsDestroyed) {
104 std::promise<void> promise1;
105 std::promise<void> promise2;
106 auto future1 = promise1.get_future();
107 auto future2 = promise2.get_future();
108 std::atomic<bool> value;
109
110 // Local scope for the worker to test its destructor when it goes out of scope.
111 {
112 WorkerThread worker(2 /*maxQueueSize*/);
113
114 ASSERT_TRUE(worker.schedule(Callable::from([promise = std::move(promise1)]() mutable {
115 promise.set_value();
116 std::this_thread::sleep_for(200ms);
117 })));
118
119 // The first task should start executing.
120 future1.wait();
121
122 // The second task should schedule successfully.
123 ASSERT_TRUE(
124 worker.schedule(Callable::from([promise = std::move(promise2), &value]() mutable {
125 // The worker should destruct before it gets a chance to execute this.
126 value = true;
127 promise.set_value();
128 })));
129 }
130
131 // The second task should never execute.
132 future2.wait();
133 // The future is expected to be ready but contain an exception.
134 // Cannot use ASSERT_THROW because exceptions are disabled in this codebase.
135 // ASSERT_THROW(future2.get(), std::future_error);
136 EXPECT_FALSE(value);
137 }
138
139 } // namespace
140