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 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 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 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 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 151 TEST(IOEventLoop, periodic) { 152 TestPeriodicEvents(1000000, 1, false); 153 } 154 155 TEST(IOEventLoop, periodic_precise) { 156 TestPeriodicEvents(1000, 100, true); 157 } 158 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 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 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 247 TEST(IOEventLoop, exit_before_loop) { 248 IOEventLoop loop; 249 ASSERT_TRUE(loop.ExitLoop()); 250 } 251