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