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