xref: /aosp_15_r20/external/ot-br-posix/tests/gtest/test_task_runner.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
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