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