blob: 468deed4e5444380cbb9414f206c668486fcfa14 [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 Hungf8ab0932022-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 Hung2aa15102022-06-13 19:49:43 -070054 TimerThread::Handle handle =
Atneya Nairf5b68512022-05-23 20:02:49 -040055 thread.scheduleTask("Basic", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -070056 taskRan = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Andy Hung2aa15102022-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);
Andy Hungcb90d202022-05-23 18:19:42 -070061 ASSERT_TRUE(taskRan); // timed-out called.
62 ASSERT_EQ(1ul, countChars(thread.timeoutToString(), REQUEST_START));
63 // nothing cancelled
64 ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080065}
66
Andy Hungf8ab0932022-06-13 19:49:43 -070067static void testCancel() {
68 const auto frac = GetParam();
69
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080070 std::atomic<bool> taskRan = false;
71 TimerThread thread;
Andy Hunga2a1ac32022-03-18 16:12:11 -070072 TimerThread::Handle handle =
Atneya Nairf5b68512022-05-23 20:02:49 -040073 thread.scheduleTask("Cancel", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -070074 taskRan = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Andy Hung2aa15102022-06-13 19:49:43 -070075 ASSERT_TRUE(TimerThread::isTimeoutHandle(handle));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080076 std::this_thread::sleep_for(100ms - kJitter);
77 ASSERT_FALSE(taskRan);
Andy Hunga2a1ac32022-03-18 16:12:11 -070078 ASSERT_TRUE(thread.cancelTask(handle));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080079 std::this_thread::sleep_for(2 * kJitter);
Andy Hungcb90d202022-05-23 18:19:42 -070080 ASSERT_FALSE(taskRan); // timed-out did not call.
81 ASSERT_EQ(0ul, countChars(thread.timeoutToString(), REQUEST_START));
82 // task cancelled.
83 ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080084}
85
Andy Hungf8ab0932022-06-13 19:49:43 -070086static void testCancelAfterRun() {
87 const auto frac = GetParam();
88
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080089 std::atomic<bool> taskRan = false;
90 TimerThread thread;
Andy Hunga2a1ac32022-03-18 16:12:11 -070091 TimerThread::Handle handle =
Andy Hung2aa15102022-06-13 19:49:43 -070092 thread.scheduleTask("CancelAfterRun",
Atneya Nairf5b68512022-05-23 20:02:49 -040093 [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -070094 taskRan = true; },
95 DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Andy Hung2aa15102022-06-13 19:49:43 -070096 ASSERT_TRUE(TimerThread::isTimeoutHandle(handle));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -080097 std::this_thread::sleep_for(100ms + kJitter);
Andy Hungcb90d202022-05-23 18:19:42 -070098 ASSERT_TRUE(taskRan); // timed-out called.
Andy Hunga2a1ac32022-03-18 16:12:11 -070099 ASSERT_FALSE(thread.cancelTask(handle));
Andy Hungcb90d202022-05-23 18:19:42 -0700100 ASSERT_EQ(1ul, countChars(thread.timeoutToString(), REQUEST_START));
101 // nothing actually cancelled
102 ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800103}
104
Andy Hungf8ab0932022-06-13 19:49:43 -0700105static void testMultipleTasks() {
106 const auto frac = GetParam();
107
Andy Hunga2a1ac32022-03-18 16:12:11 -0700108 std::array<std::atomic<bool>, 6> taskRan{};
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800109 TimerThread thread;
110
111 auto startTime = std::chrono::steady_clock::now();
112
Atneya Nairf5b68512022-05-23 20:02:49 -0400113 thread.scheduleTask("0", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -0700114 taskRan[0] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(300, frac));
Atneya Nairf5b68512022-05-23 20:02:49 -0400115 thread.scheduleTask("1", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -0700116 taskRan[1] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(100, frac));
Atneya Nairf5b68512022-05-23 20:02:49 -0400117 thread.scheduleTask("2", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -0700118 taskRan[2] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(200, frac));
Atneya Nairf5b68512022-05-23 20:02:49 -0400119 thread.scheduleTask("3", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -0700120 taskRan[3] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(400, frac));
Atneya Nairf5b68512022-05-23 20:02:49 -0400121 auto handle4 = thread.scheduleTask("4", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -0700122 taskRan[4] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(200, frac));
Atneya Nairf5b68512022-05-23 20:02:49 -0400123 thread.scheduleTask("5", [&taskRan](TimerThread::Handle) {
Andy Hungf8ab0932022-06-13 19:49:43 -0700124 taskRan[5] = true; }, DISTRIBUTE_TIMEOUT_SECONDCHANCE_MS_FRAC(200, frac));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700125
126 // 6 tasks pending
Andy Hungcb90d202022-05-23 18:19:42 -0700127 ASSERT_EQ(6ul, countChars(thread.pendingToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700128 // 0 tasks completed
Andy Hungcb90d202022-05-23 18:19:42 -0700129 ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800130
Andy Hungf8ab0932022-06-13 19:49:43 -0700131 // None of the tasks are expected to have finished at the start.
132 std::array<std::atomic<bool>, 6> expected{};
133
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800134 // Task 1 should trigger around 100ms.
135 std::this_thread::sleep_until(startTime + 100ms - kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700136
137 ASSERT_EQ(expected, taskRan);
138
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800139
140 std::this_thread::sleep_until(startTime + 100ms + kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700141
142 expected[1] = true;
143 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800144
145 // Cancel task 4 before it gets a chance to run.
146 thread.cancelTask(handle4);
147
148 // Tasks 2 and 5 should trigger around 200ms.
149 std::this_thread::sleep_until(startTime + 200ms - kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700150
151 ASSERT_EQ(expected, taskRan);
152
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800153
154 std::this_thread::sleep_until(startTime + 200ms + kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700155
156 expected[2] = true;
157 expected[5] = true;
158 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800159
160 // Task 0 should trigger around 300ms.
161 std::this_thread::sleep_until(startTime + 300ms - kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700162
163 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800164
165 std::this_thread::sleep_until(startTime + 300ms + kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700166
167 expected[0] = true;
168 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800169
Andy Hunga2a1ac32022-03-18 16:12:11 -0700170 // 1 task pending
Andy Hungcb90d202022-05-23 18:19:42 -0700171 ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
172 // 4 tasks called on timeout, and 1 cancelled
173 ASSERT_EQ(4ul, countChars(thread.timeoutToString(), REQUEST_START));
174 ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700175
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800176 // Task 3 should trigger around 400ms.
177 std::this_thread::sleep_until(startTime + 400ms - kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700178
179 ASSERT_EQ(expected, taskRan);
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800180
Andy Hungcb90d202022-05-23 18:19:42 -0700181 // 4 tasks called on timeout and 1 cancelled
182 ASSERT_EQ(4ul, countChars(thread.timeoutToString(), REQUEST_START));
183 ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700184
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800185 std::this_thread::sleep_until(startTime + 400ms + kJitter);
Andy Hungf8ab0932022-06-13 19:49:43 -0700186
187 expected[3] = true;
188 ASSERT_EQ(expected, taskRan);
Andy Hunga2a1ac32022-03-18 16:12:11 -0700189
190 // 0 tasks pending
Andy Hungcb90d202022-05-23 18:19:42 -0700191 ASSERT_EQ(0ul, countChars(thread.pendingToString(), REQUEST_START));
192 // 5 tasks called on timeout and 1 cancelled
193 ASSERT_EQ(5ul, countChars(thread.timeoutToString(), REQUEST_START));
194 ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800195}
196
Andy Hungf8ab0932022-06-13 19:49:43 -0700197}; // class TimerThreadTest
198
199TEST_P(TimerThreadTest, Basic) {
200 testBasic();
201}
202
203TEST_P(TimerThreadTest, Cancel) {
204 testCancel();
205}
206
207TEST_P(TimerThreadTest, CancelAfterRun) {
208 testCancelAfterRun();
209}
210
211TEST_P(TimerThreadTest, MultipleTasks) {
212 testMultipleTasks();
213}
214
215INSTANTIATE_TEST_CASE_P(
216 TimerThread,
217 TimerThreadTest,
218 ::testing::Values(0.f, 0.5f, 1.f)
219 );
220
Andy Hunga2a1ac32022-03-18 16:12:11 -0700221TEST(TimerThread, TrackedTasks) {
222 TimerThread thread;
223
224 auto handle0 = thread.trackTask("0");
225 auto handle1 = thread.trackTask("1");
226 auto handle2 = thread.trackTask("2");
227
Andy Hung2aa15102022-06-13 19:49:43 -0700228 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle0));
229 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle1));
230 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle2));
231
Andy Hunga2a1ac32022-03-18 16:12:11 -0700232 // 3 tasks pending
Andy Hungcb90d202022-05-23 18:19:42 -0700233 ASSERT_EQ(3ul, countChars(thread.pendingToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700234 // 0 tasks retired
Andy Hungcb90d202022-05-23 18:19:42 -0700235 ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700236
237 ASSERT_TRUE(thread.cancelTask(handle0));
238 ASSERT_TRUE(thread.cancelTask(handle1));
239
240 // 1 task pending
Andy Hungcb90d202022-05-23 18:19:42 -0700241 ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700242 // 2 tasks retired
Andy Hungcb90d202022-05-23 18:19:42 -0700243 ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700244
245 // handle1 is stale, cancel returns false.
246 ASSERT_FALSE(thread.cancelTask(handle1));
247
248 // 1 task pending
Andy Hungcb90d202022-05-23 18:19:42 -0700249 ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700250 // 2 tasks retired
Andy Hungcb90d202022-05-23 18:19:42 -0700251 ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700252
253 // Add another tracked task.
254 auto handle3 = thread.trackTask("3");
Andy Hung2aa15102022-06-13 19:49:43 -0700255 ASSERT_TRUE(TimerThread::isNoTimeoutHandle(handle3));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700256
257 // 2 tasks pending
Andy Hungcb90d202022-05-23 18:19:42 -0700258 ASSERT_EQ(2ul, countChars(thread.pendingToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700259 // 2 tasks retired
Andy Hungcb90d202022-05-23 18:19:42 -0700260 ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700261
262 ASSERT_TRUE(thread.cancelTask(handle2));
263
264 // 1 tasks pending
Andy Hungcb90d202022-05-23 18:19:42 -0700265 ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700266 // 3 tasks retired
Andy Hungcb90d202022-05-23 18:19:42 -0700267 ASSERT_EQ(3ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700268
269 ASSERT_TRUE(thread.cancelTask(handle3));
270
271 // 0 tasks pending
Andy Hungcb90d202022-05-23 18:19:42 -0700272 ASSERT_EQ(0ul, countChars(thread.pendingToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700273 // 4 tasks retired
Andy Hungcb90d202022-05-23 18:19:42 -0700274 ASSERT_EQ(4ul, countChars(thread.retiredToString(), REQUEST_START));
Andy Hunga2a1ac32022-03-18 16:12:11 -0700275}
Ytai Ben-Tsvi1ea62c92021-11-10 14:38:27 -0800276
277} // namespace