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
TEST(IOEventLoop,read)29 TEST(IOEventLoop, read) {
30 int fd[2];
31 ASSERT_EQ(0, pipe(fd));
32 IOEventLoop loop;
33 int count = 0;
34 int retry_count = 0;
35 ASSERT_NE(nullptr, loop.AddReadEvent(fd[0], [&]() {
36 while (true) {
37 char c;
38 int ret = read(fd[0], &c, 1);
39 if (ret == 1) {
40 if (++count == 100) {
41 return loop.ExitLoop();
42 }
43 } else if (ret == -1 && errno == EAGAIN) {
44 retry_count++;
45 break;
46 } else {
47 return false;
48 }
49 }
50 return true;
51 }));
52 std::thread thread([&]() {
53 for (int i = 0; i < 100; ++i) {
54 usleep(1000);
55 char c;
56 CHECK_EQ(write(fd[1], &c, 1), 1);
57 }
58 });
59 ASSERT_TRUE(loop.RunLoop());
60 thread.join();
61 ASSERT_EQ(100, count);
62 // Test retry_count to make sure we are not doing blocking read.
63 ASSERT_GT(retry_count, 0);
64 close(fd[0]);
65 close(fd[1]);
66 }
67
TEST(IOEventLoop,write)68 TEST(IOEventLoop, write) {
69 int fd[2];
70 ASSERT_EQ(0, pipe(fd));
71 IOEventLoop loop;
72 int count = 0;
73 ASSERT_NE(nullptr, loop.AddWriteEvent(fd[1], [&]() {
74 int ret = 0;
75 char buf[4096];
76 while ((ret = write(fd[1], buf, sizeof(buf))) > 0) {
77 }
78 if (ret == -1 && errno == EAGAIN) {
79 if (++count == 100) {
80 loop.ExitLoop();
81 }
82 return true;
83 }
84 return false;
85 }));
86 std::thread thread([&]() {
87 usleep(500000);
88 while (true) {
89 usleep(1000);
90 char buf[4096];
91 if (read(fd[0], buf, sizeof(buf)) <= 0) {
92 break;
93 }
94 }
95 });
96 ASSERT_TRUE(loop.RunLoop());
97 // close fd[1] to make read thread stop.
98 close(fd[1]);
99 thread.join();
100 close(fd[0]);
101 ASSERT_EQ(100, count);
102 }
103
TEST(IOEventLoop,signal)104 TEST(IOEventLoop, signal) {
105 IOEventLoop loop;
106 int count = 0;
107 ASSERT_TRUE(loop.AddSignalEvent(SIGINT, [&]() {
108 if (++count == 100) {
109 loop.ExitLoop();
110 }
111 return true;
112 }));
113 std::atomic<bool> stop_thread(false);
114 std::thread thread([&]() {
115 while (!stop_thread) {
116 usleep(1000);
117 kill(getpid(), SIGINT);
118 }
119 });
120 ASSERT_TRUE(loop.RunLoop());
121 stop_thread = true;
122 thread.join();
123 ASSERT_EQ(100, count);
124 }
125
TestPeriodicEvents(int period_in_us,int iterations,bool precise)126 void TestPeriodicEvents(int period_in_us, int iterations, bool precise) {
127 timeval tv;
128 tv.tv_sec = period_in_us / 1000000;
129 tv.tv_usec = period_in_us % 1000000;
130 int count = 0;
131 IOEventLoop loop;
132 if (precise) {
133 ASSERT_TRUE(loop.UsePreciseTimer());
134 }
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 + (precise ? 0.3 : 1);
149 ASSERT_GE(time_used, min_time_in_sec);
150 ASSERT_LT(time_used, max_time_in_sec);
151 }
152
TEST(IOEventLoop,periodic)153 TEST(IOEventLoop, periodic) {
154 TestPeriodicEvents(1000000, 1, false);
155 }
156
TEST(IOEventLoop,periodic_precise)157 TEST(IOEventLoop, periodic_precise) {
158 TestPeriodicEvents(1000, 100, true);
159 }
160
TEST(IOEventLoop,read_and_del_event)161 TEST(IOEventLoop, read_and_del_event) {
162 int fd[2];
163 ASSERT_EQ(0, pipe(fd));
164 IOEventLoop loop;
165 int count = 0;
166 IOEventRef ref = loop.AddReadEvent(fd[0], [&]() {
167 count++;
168 return IOEventLoop::DelEvent(ref);
169 });
170 ASSERT_NE(nullptr, ref);
171
172 std::thread thread([&]() {
173 for (int i = 0; i < 100; ++i) {
174 usleep(1000);
175 char c;
176 CHECK_EQ(write(fd[1], &c, 1), 1);
177 }
178 });
179 ASSERT_TRUE(loop.RunLoop());
180 thread.join();
181 ASSERT_EQ(1, count);
182 close(fd[0]);
183 close(fd[1]);
184 }
185
TEST(IOEventLoop,disable_enable_event)186 TEST(IOEventLoop, disable_enable_event) {
187 int fd[2];
188 ASSERT_EQ(0, pipe(fd));
189 IOEventLoop loop;
190 int count = 0;
191 IOEventRef ref = loop.AddWriteEvent(fd[1], [&]() {
192 count++;
193 return IOEventLoop::DisableEvent(ref);
194 });
195 ASSERT_NE(nullptr, ref);
196
197 timeval tv;
198 tv.tv_sec = 0;
199 tv.tv_usec = 500000;
200 int periodic_count = 0;
201 ASSERT_TRUE(loop.AddPeriodicEvent(tv, [&]() {
202 periodic_count++;
203 if (periodic_count == 1) {
204 if (count != 1) {
205 return false;
206 }
207 return IOEventLoop::EnableEvent(ref);
208 } else {
209 if (count != 2) {
210 return false;
211 }
212 return loop.ExitLoop();
213 }
214 }));
215
216 ASSERT_TRUE(loop.RunLoop());
217 ASSERT_EQ(2, count);
218 ASSERT_EQ(2, periodic_count);
219 close(fd[0]);
220 close(fd[1]);
221 }
222
TEST(IOEventLoop,disable_enable_periodic_event)223 TEST(IOEventLoop, disable_enable_periodic_event) {
224 timeval tv;
225 tv.tv_sec = 0;
226 tv.tv_usec = 200000;
227 IOEventLoop loop;
228 IOEventRef wait_ref = loop.AddPeriodicEvent(tv, [&]() { return loop.ExitLoop(); });
229 ASSERT_TRUE(wait_ref != nullptr);
230 ASSERT_TRUE(loop.DisableEvent(wait_ref));
231
232 tv.tv_sec = 0;
233 tv.tv_usec = 100000;
234 size_t periodic_count = 0;
235 IOEventRef ref = loop.AddPeriodicEvent(tv, [&]() {
236 if (!loop.DisableEvent(ref)) {
237 return false;
238 }
239 periodic_count++;
240 if (periodic_count < 2u) {
241 return loop.EnableEvent(ref);
242 }
243 return loop.EnableEvent(wait_ref);
244 });
245 ASSERT_TRUE(loop.RunLoop());
246 ASSERT_EQ(2u, periodic_count);
247 }
248
TEST(IOEventLoop,exit_before_loop)249 TEST(IOEventLoop, exit_before_loop) {
250 IOEventLoop loop;
251 ASSERT_TRUE(loop.ExitLoop());
252 }
253