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