xref: /aosp_15_r20/system/extras/simpleperf/IOEventLoop_test.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1 /*
2  * Copyright (C) 2016 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 "IOEventLoop.h"
18 
19 #include <gtest/gtest.h>
20 
21 #include <atomic>
22 #include <chrono>
23 #include <thread>
24 
25 #include <android-base/logging.h>
26 
27 using namespace simpleperf;
28 
29 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,read)30 TEST(IOEventLoop, read) {
31   int fd[2];
32   ASSERT_EQ(0, pipe(fd));
33   IOEventLoop loop;
34   int count = 0;
35   int retry_count = 0;
36   ASSERT_NE(nullptr, loop.AddReadEvent(fd[0], [&]() {
37     while (true) {
38       char c;
39       int ret = read(fd[0], &c, 1);
40       if (ret == 1) {
41         if (++count == 100) {
42           return loop.ExitLoop();
43         }
44       } else if (ret == -1 && errno == EAGAIN) {
45         retry_count++;
46         break;
47       } else {
48         return false;
49       }
50     }
51     return true;
52   }));
53   std::thread thread([&]() {
54     for (int i = 0; i < 100; ++i) {
55       usleep(1000);
56       char c;
57       CHECK_EQ(write(fd[1], &c, 1), 1);
58     }
59   });
60   ASSERT_TRUE(loop.RunLoop());
61   thread.join();
62   ASSERT_EQ(100, count);
63   // Test retry_count to make sure we are not doing blocking read.
64   ASSERT_GT(retry_count, 0);
65   close(fd[0]);
66   close(fd[1]);
67 }
68 
69 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,write)70 TEST(IOEventLoop, write) {
71   int fd[2];
72   ASSERT_EQ(0, pipe(fd));
73   IOEventLoop loop;
74   int count = 0;
75   ASSERT_NE(nullptr, loop.AddWriteEvent(fd[1], [&]() {
76     int ret = 0;
77     char buf[4096];
78     while ((ret = write(fd[1], buf, sizeof(buf))) > 0) {
79     }
80     if (ret == -1 && errno == EAGAIN) {
81       if (++count == 100) {
82         loop.ExitLoop();
83       }
84       return true;
85     }
86     return false;
87   }));
88   std::thread thread([&]() {
89     usleep(500000);
90     while (true) {
91       usleep(1000);
92       char buf[4096];
93       if (read(fd[0], buf, sizeof(buf)) <= 0) {
94         break;
95       }
96     }
97   });
98   ASSERT_TRUE(loop.RunLoop());
99   // close fd[1] to make read thread stop.
100   close(fd[1]);
101   thread.join();
102   close(fd[0]);
103   ASSERT_EQ(100, count);
104 }
105 
106 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,signal)107 TEST(IOEventLoop, signal) {
108   IOEventLoop loop;
109   int count = 0;
110   ASSERT_TRUE(loop.AddSignalEvent(SIGINT, [&]() {
111     if (++count == 100) {
112       loop.ExitLoop();
113     }
114     return true;
115   }));
116   std::atomic<bool> stop_thread(false);
117   std::thread thread([&]() {
118     while (!stop_thread) {
119       usleep(1000);
120       kill(getpid(), SIGINT);
121     }
122   });
123   ASSERT_TRUE(loop.RunLoop());
124   stop_thread = true;
125   thread.join();
126   ASSERT_EQ(100, count);
127 }
128 
TestPeriodicEvents(int period_in_us,int iterations)129 void TestPeriodicEvents(int period_in_us, int iterations) {
130   timeval tv;
131   tv.tv_sec = period_in_us / 1000000;
132   tv.tv_usec = period_in_us % 1000000;
133   int count = 0;
134   IOEventLoop loop;
135   ASSERT_TRUE(loop.AddPeriodicEvent(tv, [&]() {
136     if (++count == iterations) {
137       loop.ExitLoop();
138     }
139     return true;
140   }));
141   auto start_time = std::chrono::steady_clock::now();
142   ASSERT_TRUE(loop.RunLoop());
143   auto end_time = std::chrono::steady_clock::now();
144   ASSERT_EQ(iterations, count);
145   double time_used =
146       std::chrono::duration_cast<std::chrono::duration<double>>(end_time - start_time).count();
147   double min_time_in_sec = period_in_us / 1e6 * iterations;
148   double max_time_in_sec = min_time_in_sec + 0.3;
149   ASSERT_GE(time_used, min_time_in_sec);
150   ASSERT_LT(time_used, max_time_in_sec);
151 }
152 
153 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,periodic)154 TEST(IOEventLoop, periodic) {
155   TestPeriodicEvents(1000, 100);
156 }
157 
158 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,one_time_event)159 TEST(IOEventLoop, one_time_event) {
160   int duration_in_us = 1000;
161   timeval tv = {};
162   tv.tv_usec = duration_in_us;
163   int count = 0;
164   auto callback_time = std::chrono::steady_clock::now();
165   IOEventLoop loop;
166   // Add a one time event to test callback count and time.
167   ASSERT_TRUE(loop.AddOneTimeEvent(tv, [&]() {
168     ++count;
169     callback_time = std::chrono::steady_clock::now();
170     return true;
171   }));
172   // Add another one time event to exit loop.
173   tv.tv_usec = duration_in_us * 3;
174   ASSERT_TRUE(loop.AddOneTimeEvent(tv, [&]() { return loop.ExitLoop(); }));
175 
176   auto start_time = std::chrono::steady_clock::now();
177   ASSERT_TRUE(loop.RunLoop());
178   ASSERT_EQ(1, count);
179   double time_used =
180       std::chrono::duration_cast<std::chrono::duration<double>>(callback_time - start_time).count();
181   double min_time_in_sec = duration_in_us / 1e6;
182   double max_time_in_sec = min_time_in_sec + 0.3;
183   ASSERT_GE(time_used, min_time_in_sec);
184   ASSERT_LT(time_used, max_time_in_sec);
185 }
186 
187 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,read_and_del_event)188 TEST(IOEventLoop, read_and_del_event) {
189   int fd[2];
190   ASSERT_EQ(0, pipe(fd));
191   IOEventLoop loop;
192   int count = 0;
193   IOEventRef ref = loop.AddReadEvent(fd[0], [&]() {
194     count++;
195     return IOEventLoop::DelEvent(ref);
196   });
197   ASSERT_NE(nullptr, ref);
198 
199   std::thread thread([&]() {
200     for (int i = 0; i < 100; ++i) {
201       usleep(1000);
202       char c;
203       CHECK_EQ(write(fd[1], &c, 1), 1);
204     }
205   });
206   ASSERT_TRUE(loop.RunLoop());
207   thread.join();
208   ASSERT_EQ(1, count);
209   close(fd[0]);
210   close(fd[1]);
211 }
212 
213 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,disable_enable_event)214 TEST(IOEventLoop, disable_enable_event) {
215   int fd[2];
216   ASSERT_EQ(0, pipe(fd));
217   IOEventLoop loop;
218   int count = 0;
219   IOEventRef ref = loop.AddWriteEvent(fd[1], [&]() {
220     count++;
221     return IOEventLoop::DisableEvent(ref);
222   });
223   ASSERT_NE(nullptr, ref);
224 
225   timeval tv;
226   tv.tv_sec = 0;
227   tv.tv_usec = 500000;
228   int periodic_count = 0;
229   ASSERT_TRUE(loop.AddPeriodicEvent(tv, [&]() {
230     periodic_count++;
231     if (periodic_count == 1) {
232       if (count != 1) {
233         return false;
234       }
235       return IOEventLoop::EnableEvent(ref);
236     } else {
237       if (count != 2) {
238         return false;
239       }
240       return loop.ExitLoop();
241     }
242   }));
243 
244   ASSERT_TRUE(loop.RunLoop());
245   ASSERT_EQ(2, count);
246   ASSERT_EQ(2, periodic_count);
247   close(fd[0]);
248   close(fd[1]);
249 }
250 
251 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,disable_enable_periodic_event)252 TEST(IOEventLoop, disable_enable_periodic_event) {
253   timeval tv;
254   tv.tv_sec = 0;
255   tv.tv_usec = 200000;
256   IOEventLoop loop;
257   IOEventRef wait_ref = loop.AddPeriodicEvent(tv, [&]() { return loop.ExitLoop(); });
258   ASSERT_TRUE(wait_ref != nullptr);
259   ASSERT_TRUE(loop.DisableEvent(wait_ref));
260 
261   tv.tv_sec = 0;
262   tv.tv_usec = 100000;
263   size_t periodic_count = 0;
264   IOEventRef ref = loop.AddPeriodicEvent(tv, [&]() {
265     if (!loop.DisableEvent(ref)) {
266       return false;
267     }
268     periodic_count++;
269     if (periodic_count < 2u) {
270       return loop.EnableEvent(ref);
271     }
272     return loop.EnableEvent(wait_ref);
273   });
274   ASSERT_TRUE(loop.RunLoop());
275   ASSERT_EQ(2u, periodic_count);
276 }
277 
278 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,exit_before_loop)279 TEST(IOEventLoop, exit_before_loop) {
280   IOEventLoop loop;
281   ASSERT_TRUE(loop.ExitLoop());
282 }
283 
284 // @CddTest = 6.1/C-0-2
TEST(IOEventLoop,priority)285 TEST(IOEventLoop, priority) {
286   int low_priority_fd[2];
287   ASSERT_EQ(0, pipe(low_priority_fd));
288   int high_priority_fd[2];
289   ASSERT_EQ(0, pipe(high_priority_fd));
290 
291   IOEventLoop loop;
292   int count = 0;
293 
294   ASSERT_NE(nullptr, loop.AddReadEvent(
295                          low_priority_fd[0],
296                          [&]() {
297                            char c;
298                            read(low_priority_fd[0], &c, 1);
299                            CHECK_EQ(count, 1);
300                            count++;
301                            return loop.ExitLoop();
302                          },
303                          IOEventLowPriority));
304 
305   ASSERT_NE(nullptr, loop.AddReadEvent(
306                          high_priority_fd[0],
307                          [&]() {
308                            char c;
309                            read(high_priority_fd[0], &c, 1);
310                            CHECK_EQ(count, 0);
311                            count++;
312                            return true;
313                          },
314                          IOEventHighPriority));
315 
316   char c;
317   CHECK_EQ(write(low_priority_fd[1], &c, 1), 1);
318   CHECK_EQ(write(high_priority_fd[1], &c, 1), 1);
319   ASSERT_TRUE(loop.RunLoop());
320   ASSERT_EQ(2, count);
321   for (int i = 0; i < 2; i++) {
322     close(low_priority_fd[i]);
323     close(high_priority_fd[i]);
324   }
325 }
326