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