1 /*
2 * Copyright (c) 2021, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <atomic>
30 #include <mutex>
31 #include <thread>
32
33 #include <gtest/gtest.h>
34 #include <unistd.h>
35
36 #include "common/task_runner.hpp"
37
TEST(TaskRunner,TestSingleThread)38 TEST(TaskRunner, TestSingleThread)
39 {
40 int rval;
41 int counter = 0;
42 otbr::MainloopContext mainloop;
43 otbr::TaskRunner taskRunner;
44
45 mainloop.mMaxFd = -1;
46 mainloop.mTimeout = {10, 0};
47
48 FD_ZERO(&mainloop.mReadFdSet);
49 FD_ZERO(&mainloop.mWriteFdSet);
50 FD_ZERO(&mainloop.mErrorFdSet);
51
52 // Increase the `counter` to 3.
53 taskRunner.Post([&]() {
54 ++counter;
55 taskRunner.Post([&]() {
56 ++counter;
57 taskRunner.Post([&]() { ++counter; });
58 });
59 });
60
61 taskRunner.Update(mainloop);
62 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
63 &mainloop.mTimeout);
64 EXPECT_EQ(1, rval);
65
66 taskRunner.Process(mainloop);
67 EXPECT_EQ(3, counter);
68 }
69
TEST(TaskRunner,TestTasksOrder)70 TEST(TaskRunner, TestTasksOrder)
71 {
72 std::string str;
73 otbr::TaskRunner taskRunner;
74 int rval;
75 otbr::MainloopContext mainloop;
76
77 taskRunner.Post([&]() { str.push_back('a'); });
78 taskRunner.Post([&]() { str.push_back('b'); });
79 taskRunner.Post([&]() { str.push_back('c'); });
80
81 mainloop.mMaxFd = -1;
82 mainloop.mTimeout = {2, 0};
83
84 FD_ZERO(&mainloop.mReadFdSet);
85 FD_ZERO(&mainloop.mWriteFdSet);
86 FD_ZERO(&mainloop.mErrorFdSet);
87
88 taskRunner.Update(mainloop);
89 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
90 &mainloop.mTimeout);
91 EXPECT_EQ(rval, 1);
92
93 taskRunner.Process(mainloop);
94
95 // Make sure the tasks are executed in the order of posting.
96 EXPECT_STREQ("abc", str.c_str());
97 }
98
TEST(TaskRunner,TestMultipleThreads)99 TEST(TaskRunner, TestMultipleThreads)
100 {
101 std::atomic<int> counter{0};
102 otbr::TaskRunner taskRunner;
103 std::vector<std::thread> threads;
104
105 // Increase the `counter` to 10 in separate threads.
106 for (size_t i = 0; i < 10; ++i)
107 {
108 threads.emplace_back([&]() { taskRunner.Post([&]() { ++counter; }); });
109 }
110
111 while (counter.load() < 10)
112 {
113 int rval;
114 otbr::MainloopContext mainloop;
115
116 mainloop.mMaxFd = -1;
117 mainloop.mTimeout = {10, 0};
118
119 FD_ZERO(&mainloop.mReadFdSet);
120 FD_ZERO(&mainloop.mWriteFdSet);
121 FD_ZERO(&mainloop.mErrorFdSet);
122
123 taskRunner.Update(mainloop);
124 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
125 &mainloop.mTimeout);
126 EXPECT_EQ(1, rval);
127
128 taskRunner.Process(mainloop);
129 }
130
131 for (auto &th : threads)
132 {
133 th.join();
134 }
135
136 EXPECT_EQ(10, counter.load());
137 }
138
TEST(TaskRunner,TestPostAndWait)139 TEST(TaskRunner, TestPostAndWait)
140 {
141 std::atomic<int> total{0};
142 std::atomic<int> counter{0};
143 otbr::TaskRunner taskRunner;
144 std::vector<std::thread> threads;
145
146 // Increase the `counter` to 10 in separate threads and accumulate the total value.
147 for (size_t i = 0; i < 10; ++i)
148 {
149 threads.emplace_back([&]() { total += taskRunner.PostAndWait<int>([&]() { return ++counter; }); });
150 }
151
152 while (counter.load() < 10)
153 {
154 int rval;
155 otbr::MainloopContext mainloop;
156
157 mainloop.mMaxFd = -1;
158 mainloop.mTimeout = {10, 0};
159
160 FD_ZERO(&mainloop.mReadFdSet);
161 FD_ZERO(&mainloop.mWriteFdSet);
162 FD_ZERO(&mainloop.mErrorFdSet);
163
164 taskRunner.Update(mainloop);
165 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
166 &mainloop.mTimeout);
167 EXPECT_EQ(1, rval);
168
169 taskRunner.Process(mainloop);
170 }
171
172 for (auto &th : threads)
173 {
174 th.join();
175 }
176
177 EXPECT_EQ(55, total);
178 EXPECT_EQ(10, counter.load());
179 }
180
TEST(TaskRunner,TestDelayedTasks)181 TEST(TaskRunner, TestDelayedTasks)
182 {
183 std::atomic<int> counter{0};
184 otbr::TaskRunner taskRunner;
185 std::vector<std::thread> threads;
186
187 // Increase the `counter` to 10 in separate threads.
188 for (size_t i = 0; i < 10; ++i)
189 {
190 threads.emplace_back([&]() { taskRunner.Post(std::chrono::milliseconds(10), [&]() { ++counter; }); });
191 }
192
193 while (counter.load() < 10)
194 {
195 int rval;
196 otbr::MainloopContext mainloop;
197
198 mainloop.mMaxFd = -1;
199 mainloop.mTimeout = {2, 0};
200
201 FD_ZERO(&mainloop.mReadFdSet);
202 FD_ZERO(&mainloop.mWriteFdSet);
203 FD_ZERO(&mainloop.mErrorFdSet);
204
205 taskRunner.Update(mainloop);
206 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
207 &mainloop.mTimeout);
208 EXPECT_TRUE(rval >= 0 || errno == EINTR);
209
210 taskRunner.Process(mainloop);
211 }
212
213 for (auto &th : threads)
214 {
215 th.join();
216 }
217
218 EXPECT_EQ(10, counter.load());
219 }
220
TEST(TaskRunner,TestDelayedTasksOrder)221 TEST(TaskRunner, TestDelayedTasksOrder)
222 {
223 std::string str;
224 otbr::TaskRunner taskRunner;
225
226 taskRunner.Post(std::chrono::milliseconds(10), [&]() { str.push_back('a'); });
227 taskRunner.Post(std::chrono::milliseconds(9), [&]() { str.push_back('b'); });
228 taskRunner.Post(std::chrono::milliseconds(10), [&]() { str.push_back('c'); });
229
230 while (str.size() < 3)
231 {
232 int rval;
233 otbr::MainloopContext mainloop;
234
235 mainloop.mMaxFd = -1;
236 mainloop.mTimeout = {2, 0};
237
238 FD_ZERO(&mainloop.mReadFdSet);
239 FD_ZERO(&mainloop.mWriteFdSet);
240 FD_ZERO(&mainloop.mErrorFdSet);
241
242 taskRunner.Update(mainloop);
243 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
244 &mainloop.mTimeout);
245 EXPECT_TRUE(rval >= 0 || errno == EINTR);
246
247 taskRunner.Process(mainloop);
248 }
249
250 // Make sure that tasks with smaller delay are executed earlier.
251 EXPECT_STREQ("bac", str.c_str());
252 }
253
TEST(TaskRunner,TestCancelDelayedTasks)254 TEST(TaskRunner, TestCancelDelayedTasks)
255 {
256 std::string str;
257 otbr::TaskRunner taskRunner;
258 otbr::TaskRunner::TaskId tid1, tid2, tid3, tid4, tid5;
259
260 tid1 = taskRunner.Post(std::chrono::milliseconds(10), [&]() { str.push_back('a'); });
261 tid2 = taskRunner.Post(std::chrono::milliseconds(20), [&]() { str.push_back('b'); });
262 tid3 = taskRunner.Post(std::chrono::milliseconds(30), [&]() { str.push_back('c'); });
263 tid4 = taskRunner.Post(std::chrono::milliseconds(40), [&]() { str.push_back('d'); });
264 tid5 = taskRunner.Post(std::chrono::milliseconds(50), [&]() { str.push_back('e'); });
265
266 EXPECT_TRUE(0 < tid1);
267 EXPECT_TRUE(tid1 < tid2);
268 EXPECT_TRUE(tid2 < tid3);
269 EXPECT_TRUE(tid3 < tid4);
270 EXPECT_TRUE(tid4 < tid5);
271
272 taskRunner.Cancel(tid2);
273
274 taskRunner.Post(std::chrono::milliseconds(10), [&]() { taskRunner.Cancel(tid3); });
275 std::thread t([&]() {
276 usleep(20);
277 taskRunner.Cancel(tid4);
278 });
279
280 while (str.size() < 2)
281 {
282 int rval;
283 otbr::MainloopContext mainloop;
284
285 mainloop.mMaxFd = -1;
286 mainloop.mTimeout = {2, 0};
287
288 FD_ZERO(&mainloop.mReadFdSet);
289 FD_ZERO(&mainloop.mWriteFdSet);
290 FD_ZERO(&mainloop.mErrorFdSet);
291
292 taskRunner.Update(mainloop);
293 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
294 &mainloop.mTimeout);
295 EXPECT_TRUE(rval >= 0 || errno == EINTR);
296
297 taskRunner.Process(mainloop);
298 }
299
300 // Make sure the delayed task was not executed.
301 EXPECT_STREQ("ae", str.c_str());
302
303 // Make sure it's fine to cancel expired task IDs.
304 taskRunner.Cancel(tid1);
305 taskRunner.Cancel(tid2);
306 t.join();
307 }
308
TEST(TaskRunner,TestAllAPIs)309 TEST(TaskRunner, TestAllAPIs)
310 {
311 std::atomic<int> counter{0};
312 otbr::TaskRunner taskRunner;
313 std::vector<std::thread> threads;
314
315 // Increase the `counter` to 30 in separate threads.
316 for (size_t i = 0; i < 10; ++i)
317 {
318 threads.emplace_back([&]() { taskRunner.Post([&]() { ++counter; }); });
319 threads.emplace_back([&]() { taskRunner.Post(std::chrono::milliseconds(10), [&]() { ++counter; }); });
320 threads.emplace_back([&]() { taskRunner.PostAndWait<int>([&]() { return ++counter; }); });
321 }
322
323 while (counter.load() < 30)
324 {
325 int rval;
326 otbr::MainloopContext mainloop;
327
328 mainloop.mMaxFd = -1;
329 mainloop.mTimeout = {2, 0};
330
331 FD_ZERO(&mainloop.mReadFdSet);
332 FD_ZERO(&mainloop.mWriteFdSet);
333 FD_ZERO(&mainloop.mErrorFdSet);
334
335 taskRunner.Update(mainloop);
336 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
337 &mainloop.mTimeout);
338 EXPECT_TRUE(rval >= 0 || errno == EINTR);
339
340 taskRunner.Process(mainloop);
341 }
342
343 for (auto &th : threads)
344 {
345 th.join();
346 }
347
348 EXPECT_EQ(30, counter.load());
349 }
350