blob: 9452c075dcbb0729286e6ed439a10a4de9a02be4 [file] [log] [blame]
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -08001/*
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 <chrono>
18#include <thread>
19#include <gtest/gtest.h>
20#include <mediautils/TimerThread.h>
21
22using namespace std::chrono_literals;
Andy Hunga2a1ac32022-03-18 16:12:11 -070023using namespace android::mediautils;
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080024
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080025namespace {
26
27constexpr auto kJitter = 10ms;
28
Andy Hunga2a1ac32022-03-18 16:12:11 -070029// Each task written by *ToString() will start with a left brace.
30constexpr char REQUEST_START = '{';
31
32inline size_t countChars(std::string_view s, char c) {
33 return std::count(s.begin(), s.end(), c);
34}
35
Andy Hung741b3dd2022-06-13 19:49:43 -070036
37// Split msec time between timeout and second chance time
38// This tests expiration times weighted between timeout and the second chance time.
39#define DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(msec, frac) \
40 std::chrono::milliseconds(int((msec) * (frac)) + 1), \
41 std::chrono::milliseconds(int((msec) * (1.f - (frac))))
42
43// The TimerThreadTest is parameterized on a fraction between 0.f and 1.f which
44// is how the total timeout time is split between the first timeout and the second chance time.
45//
46class TimerThreadTest : public ::testing::TestWithParam<float> {
47protected:
48
49static void testBasic() {
50 const auto frac = GetParam();
51
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080052 std::atomic<bool> taskRan = false;
53 TimerThread thread;
Andy Hungdf1ed5c2022-06-13 19:49:43 -070054 TimerThread::Handle handle =
55 thread.scheduleTask("Basic", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -070056 taskRan = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -070057 ASSERT_TRUE(TimerThread::isTimeoutHandle(handle));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080058 std::this_thread::sleep_for(100ms - kJitter);
59 ASSERT_FALSE(taskRan);
60 std::this_thread::sleep_for(2 * kJitter);
61 ASSERT_TRUE(taskRan);
Andy Hunga2a1ac32022-03-18 16:12:11 -070062 ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080063}
64
Andy Hung741b3dd2022-06-13 19:49:43 -070065static void testCancel() {
66 const auto frac = GetParam();
67
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080068 std::atomic<bool> taskRan = false;
69 TimerThread thread;
Andy Hunga2a1ac32022-03-18 16:12:11 -070070 TimerThread::Handle handle =
Andy Hungdf1ed5c2022-06-13 19:49:43 -070071 thread.scheduleTask("Cancel", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -070072 taskRan = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -070073 ASSERT_TRUE(TimerThread::isTimeoutHandle(handle));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080074 std::this_thread::sleep_for(100ms - kJitter);
75 ASSERT_FALSE(taskRan);
Andy Hunga2a1ac32022-03-18 16:12:11 -070076 ASSERT_TRUE(thread.cancelTask(handle));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080077 std::this_thread::sleep_for(2 * kJitter);
78 ASSERT_FALSE(taskRan);
Andy Hunga2a1ac32022-03-18 16:12:11 -070079 ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080080}
81
Andy Hung741b3dd2022-06-13 19:49:43 -070082static void testCancelAfterRun() {
83 const auto frac = GetParam();
84
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080085 std::atomic<bool> taskRan = false;
86 TimerThread thread;
Andy Hunga2a1ac32022-03-18 16:12:11 -070087 TimerThread::Handle handle =
Andy Hungdf1ed5c2022-06-13 19:49:43 -070088 thread.scheduleTask("CancelAfterRun",
89 [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -070090 taskRan = true; },
91 DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -070092 ASSERT_TRUE(TimerThread::isTimeoutHandle(handle));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080093 std::this_thread::sleep_for(100ms + kJitter);
94 ASSERT_TRUE(taskRan);
Andy Hunga2a1ac32022-03-18 16:12:11 -070095 ASSERT_FALSE(thread.cancelTask(handle));
96 ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080097}
98
Andy Hung741b3dd2022-06-13 19:49:43 -070099static void testMultipleTasks() {
100 const auto frac = GetParam();
101
Andy Hunga2a1ac32022-03-18 16:12:11 -0700102 std::array<std::atomic<bool>, 6> taskRan{};
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800103 TimerThread thread;
104
105 auto startTime = std::chrono::steady_clock::now();
106
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700107 thread.scheduleTask("0", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -0700108 taskRan[0] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(300, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700109 thread.scheduleTask("1", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -0700110 taskRan[1] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700111 thread.scheduleTask("2", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -0700112 taskRan[2] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(200, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700113 thread.scheduleTask("3", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -0700114 taskRan[3] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(400, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700115 auto handle4 = thread.scheduleTask("4", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -0700116 taskRan[4] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(200, frac));
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700117 thread.scheduleTask("5", [&taskRan](TimerThread::Handle handle __unused) {
Andy Hung741b3dd2022-06-13 19:49:43 -0700118 taskRan[5] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(200, frac));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700119
120 // 6 tasks pending
121 ASSERT_EQ(6, countChars(thread.pendingToString(), REQUEST_START));
122 // 0 tasks completed
123 ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800124
Andy Hung741b3dd2022-06-13 19:49:43 -0700125 // None of the tasks are expected to have finished at the start.
126 std::array<std::atomic<bool>, 6> expected{};
127
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800128 // Task 1 should trigger around 100ms.
129 std::this_thread::sleep_until(startTime + 100ms - kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700130
131 ASSERT_EQ(expected, taskRan);
132
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800133
134 std::this_thread::sleep_until(startTime + 100ms + kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700135
136 expected[1] = true;
137 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800138
139 // Cancel task 4 before it gets a chance to run.
140 thread.cancelTask(handle4);
141
142 // Tasks 2 and 5 should trigger around 200ms.
143 std::this_thread::sleep_until(startTime + 200ms - kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700144
145 ASSERT_EQ(expected, taskRan);
146
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800147
148 std::this_thread::sleep_until(startTime + 200ms + kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700149
150 expected[2] = true;
151 expected[5] = true;
152 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800153
154 // Task 0 should trigger around 300ms.
155 std::this_thread::sleep_until(startTime + 300ms - kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700156
157 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800158
159 std::this_thread::sleep_until(startTime + 300ms + kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700160
161 expected[0] = true;
162 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800163
Andy Hunga2a1ac32022-03-18 16:12:11 -0700164 // 1 task pending
165 ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
166 // 4 tasks ran and 1 cancelled
167 ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
168
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800169 // Task 3 should trigger around 400ms.
170 std::this_thread::sleep_until(startTime + 400ms - kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700171
172 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800173
Andy Hunga2a1ac32022-03-18 16:12:11 -0700174 // 4 tasks ran and 1 cancelled
175 ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
176
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800177 std::this_thread::sleep_until(startTime + 400ms + kJitter);
Andy Hung741b3dd2022-06-13 19:49:43 -0700178
179 expected[3] = true;
180 ASSERT_EQ(expected, taskRan);
Andy Hunga2a1ac32022-03-18 16:12:11 -0700181
182 // 0 tasks pending
183 ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
184 // 5 tasks ran and 1 cancelled
185 ASSERT_EQ(5 + 1, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800186}
187
Andy Hung741b3dd2022-06-13 19:49:43 -0700188}; // class TimerThreadTest
189
190TEST_P(TimerThreadTest, Basic) {
191 testBasic();
192}
193
194TEST_P(TimerThreadTest, Cancel) {
195 testCancel();
196}
197
198TEST_P(TimerThreadTest, CancelAfterRun) {
199 testCancelAfterRun();
200}
201
202TEST_P(TimerThreadTest, MultipleTasks) {
203 testMultipleTasks();
204}
205
206INSTANTIATE_TEST_CASE_P(
207 TimerThread,
208 TimerThreadTest,
209 ::testing::Values(0.f, 0.5f, 1.f)
210 );
211
Andy Hunga2a1ac32022-03-18 16:12:11 -0700212TEST(TimerThread, TrackedTasks) {
213 TimerThread thread;
214
215 auto handle0 = thread.trackTask("0");
216 auto handle1 = thread.trackTask("1");
217 auto handle2 = thread.trackTask("2");
218
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700219 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle0));
220 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle1));
221 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle2));
222
Andy Hunga2a1ac32022-03-18 16:12:11 -0700223 // 3 tasks pending
224 ASSERT_EQ(3, countChars(thread.pendingToString(), REQUEST_START));
225 // 0 tasks retired
226 ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
227
228 ASSERT_TRUE(thread.cancelTask(handle0));
229 ASSERT_TRUE(thread.cancelTask(handle1));
230
231 // 1 task pending
232 ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
233 // 2 tasks retired
234 ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
235
236 // handle1 is stale, cancel returns false.
237 ASSERT_FALSE(thread.cancelTask(handle1));
238
239 // 1 task pending
240 ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
241 // 2 tasks retired
242 ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
243
244 // Add another tracked task.
245 auto handle3 = thread.trackTask("3");
Andy Hungdf1ed5c2022-06-13 19:49:43 -0700246 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle3));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700247
248 // 2 tasks pending
249 ASSERT_EQ(2, countChars(thread.pendingToString(), REQUEST_START));
250 // 2 tasks retired
251 ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
252
253 ASSERT_TRUE(thread.cancelTask(handle2));
254
255 // 1 tasks pending
256 ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
257 // 3 tasks retired
258 ASSERT_EQ(3, countChars(thread.retiredToString(), REQUEST_START));
259
260 ASSERT_TRUE(thread.cancelTask(handle3));
261
262 // 0 tasks pending
263 ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
264 // 4 tasks retired
265 ASSERT_EQ(4, countChars(thread.retiredToString(), REQUEST_START));
266}
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800267
268} // namespace