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