1 /* Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <pthread.h> 9 #include <stdio.h> 10 #include <sys/ioctl.h> 11 #include <sys/stat.h> 12 #include <sys/time.h> 13 14 #include "gtest/gtest.h" 15 16 #include "nacl_io/event_emitter.h" 17 #include "nacl_io/event_listener.h" 18 #include "nacl_io/event_listener.h" 19 #include "nacl_io/event_listener.h" 20 #include "nacl_io/kernel_intercept.h" 21 #include "nacl_io/kernel_proxy.h" 22 #include "nacl_io/kernel_wrap.h" 23 #include "nacl_io/pipe/pipe_node.h" 24 #include "nacl_io/stream/stream_fs.h" 25 26 #include "ppapi_simple/ps.h" 27 28 using namespace nacl_io; 29 using namespace sdk_util; 30 31 class EventListenerTester : public EventListener { 32 public: 33 EventListenerTester() : EventListener(), events_(0) {}; 34 35 virtual void ReceiveEvents(EventEmitter* emitter, uint32_t events) { 36 events_ |= events; 37 } 38 39 uint32_t Events() { return events_; } 40 41 void Clear() { events_ = 0; } 42 43 uint32_t events_; 44 }; 45 46 TEST(EmitterBasic, SingleThread) { 47 EventListenerTester listener_a; 48 EventListenerTester listener_b; 49 EventEmitter emitter; 50 51 emitter.RegisterListener(&listener_a, POLLIN | POLLOUT | POLLERR); 52 emitter.RegisterListener(&listener_b, POLLIN | POLLOUT | POLLERR); 53 54 EXPECT_EQ(0, emitter.GetEventStatus()); 55 EXPECT_EQ(0, listener_a.Events()); 56 57 { 58 AUTO_LOCK(emitter.GetLock()) 59 emitter.RaiseEvents_Locked(POLLIN); 60 } 61 EXPECT_EQ(POLLIN, listener_a.Events()); 62 63 listener_a.Clear(); 64 65 { 66 AUTO_LOCK(emitter.GetLock()) 67 emitter.RaiseEvents_Locked(POLLOUT); 68 } 69 EXPECT_EQ(POLLOUT, listener_a.Events()); 70 EXPECT_EQ(POLLIN | POLLOUT, listener_b.Events()); 71 } 72 73 class EmitterTest : public ::testing::Test { 74 public: 75 void SetUp() { 76 pthread_cond_init(&multi_cond_, NULL); 77 waiting_ = 0; 78 signaled_ = 0; 79 } 80 81 void TearDown() { pthread_cond_destroy(&multi_cond_); } 82 83 pthread_t CreateThread() { 84 pthread_t id; 85 EXPECT_EQ(0, pthread_create(&id, NULL, ThreadThunk, this)); 86 return id; 87 } 88 89 static void* ThreadThunk(void* ptr) { 90 return static_cast<EmitterTest*>(ptr)->ThreadEntry(); 91 } 92 93 void* ThreadEntry() { 94 EventListenerLock listener(&emitter_); 95 96 pthread_cond_signal(&multi_cond_); 97 waiting_++; 98 EXPECT_EQ(0, listener.WaitOnEvent(POLLIN, -1)); 99 emitter_.ClearEvents_Locked(POLLIN); 100 AUTO_LOCK(signaled_lock_); 101 signaled_++; 102 return NULL; 103 } 104 105 int GetSignaledCount() { 106 AUTO_LOCK(signaled_lock_); 107 return signaled_; 108 } 109 110 protected: 111 pthread_cond_t multi_cond_; 112 EventEmitter emitter_; 113 int waiting_; 114 115 private: 116 int signaled_; 117 sdk_util::SimpleLock signaled_lock_; 118 }; 119 120 // Temporarily disabled since it seems to be causing lockup in whe 121 // KernelWrapTests later on. 122 // TODO(sbc): renable once we fix http://crbug.com/378596 123 const int NUM_THREADS = 10; 124 TEST_F(EmitterTest, DISABLED_MultiThread) { 125 pthread_t threads[NUM_THREADS]; 126 127 for (int a = 0; a < NUM_THREADS; a++) 128 threads[a] = CreateThread(); 129 130 { 131 AUTO_LOCK(emitter_.GetLock()); 132 133 // Wait for all threads to wait 134 while (waiting_ < NUM_THREADS) 135 pthread_cond_wait(&multi_cond_, emitter_.GetLock().mutex()); 136 137 ASSERT_EQ(0, GetSignaledCount()); 138 139 emitter_.RaiseEvents_Locked(POLLIN); 140 } 141 142 // sleep for 50 milliseconds 143 struct timespec sleeptime = {0, 50 * 1000 * 1000}; 144 nanosleep(&sleeptime, NULL); 145 146 EXPECT_EQ(1, GetSignaledCount()); 147 148 { 149 AUTO_LOCK(emitter_.GetLock()); 150 emitter_.RaiseEvents_Locked(POLLIN); 151 } 152 153 nanosleep(&sleeptime, NULL); 154 EXPECT_EQ(2, GetSignaledCount()); 155 156 // Clean up remaining threads. 157 while (GetSignaledCount() < waiting_) { 158 AUTO_LOCK(emitter_.GetLock()); 159 emitter_.RaiseEvents_Locked(POLLIN); 160 } 161 162 for (int a = 0; a < NUM_THREADS; a++) 163 pthread_join(threads[a], NULL); 164 } 165 166 TEST(EventListenerPollTest, WaitForAny) { 167 ScopedEventEmitter emitter1(new EventEmitter()); 168 ScopedEventEmitter emitter2(new EventEmitter()); 169 ScopedEventEmitter emitter3(new EventEmitter()); 170 EventListenerPoll listener; 171 EventRequest requests[3] = { 172 {emitter1, 0, 0}, {emitter2, 0, 0}, {emitter3, 0, 0}, }; 173 Error error = 174 listener.WaitOnAny(requests, sizeof(requests) / sizeof(requests[0]), 1); 175 ASSERT_EQ(ETIMEDOUT, error); 176 } 177 178 TEST(PipeTest, Listener) { 179 const char hello[] = "Hello World."; 180 char tmp[64] = "Goodbye"; 181 182 PipeEventEmitter pipe(32); 183 184 // Expect to time out on input. 185 { 186 EventListenerLock locker(&pipe); 187 EXPECT_EQ(ETIMEDOUT, locker.WaitOnEvent(POLLIN, 0)); 188 } 189 190 // Output should be ready to go. 191 { 192 EventListenerLock locker(&pipe); 193 EXPECT_EQ(0, locker.WaitOnEvent(POLLOUT, 0)); 194 int out_bytes = 0; 195 EXPECT_EQ(0, pipe.Write_Locked(hello, sizeof(hello), &out_bytes)); 196 EXPECT_EQ(sizeof(hello), out_bytes); 197 } 198 199 // We should now be able to poll 200 { 201 EventListenerLock locker(&pipe); 202 EXPECT_EQ(0, locker.WaitOnEvent(POLLIN, 0)); 203 int out_bytes = -1; 204 EXPECT_EQ(0, pipe.Read_Locked(tmp, sizeof(tmp), &out_bytes)); 205 EXPECT_EQ(sizeof(hello), out_bytes); 206 } 207 208 // Verify we can read it correctly. 209 EXPECT_EQ(0, strcmp(hello, tmp)); 210 } 211 212 class StreamFsForTesting : public StreamFs { 213 public: 214 StreamFsForTesting() {} 215 }; 216 217 TEST(PipeNodeTest, Basic) { 218 ScopedFilesystem fs(new StreamFsForTesting()); 219 220 PipeNode* pipe_node = new PipeNode(fs.get()); 221 ScopedRef<PipeNode> pipe(pipe_node); 222 223 EXPECT_EQ(POLLOUT, pipe_node->GetEventStatus()); 224 } 225 226 const int MAX_FDS = 32; 227 class SelectPollTest : public ::testing::Test { 228 public: 229 void SetUp() { 230 kp = new KernelProxy(); 231 kp->Init(NULL); 232 EXPECT_EQ(0, kp->umount("/")); 233 EXPECT_EQ(0, kp->mount("", "/", "memfs", 0, NULL)); 234 235 memset(&tv, 0, sizeof(tv)); 236 } 237 238 void TearDown() { delete kp; } 239 240 void SetFDs(int* fds, int cnt) { 241 FD_ZERO(&rd_set); 242 FD_ZERO(&wr_set); 243 FD_ZERO(&ex_set); 244 245 for (int index = 0; index < cnt; index++) { 246 EXPECT_NE(-1, fds[index]); 247 FD_SET(fds[index], &rd_set); 248 FD_SET(fds[index], &wr_set); 249 FD_SET(fds[index], &ex_set); 250 251 pollfds[index].fd = fds[index]; 252 pollfds[index].events = POLLIN | POLLOUT; 253 pollfds[index].revents = -1; 254 } 255 } 256 257 void CloseFDs(int* fds, int cnt) { 258 for (int index = 0; index < cnt; index++) 259 kp->close(fds[index]); 260 } 261 262 protected: 263 KernelProxy* kp; 264 265 timeval tv; 266 fd_set rd_set; 267 fd_set wr_set; 268 fd_set ex_set; 269 struct pollfd pollfds[MAX_FDS]; 270 }; 271 272 TEST_F(SelectPollTest, PollMemPipe) { 273 int fds[2]; 274 275 // Both FDs for regular files should be read/write but not exception. 276 fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); 277 fds[1] = kp->open("/test.txt", O_RDONLY); 278 ASSERT_GT(fds[0], -1); 279 ASSERT_GT(fds[1], -1); 280 281 SetFDs(fds, 2); 282 283 ASSERT_EQ(2, kp->poll(pollfds, 2, 0)); 284 ASSERT_EQ(POLLIN | POLLOUT, pollfds[0].revents); 285 ASSERT_EQ(POLLIN | POLLOUT, pollfds[1].revents); 286 CloseFDs(fds, 2); 287 288 // The write FD should select for write-only, read FD should not select 289 ASSERT_EQ(0, kp->pipe(fds)); 290 SetFDs(fds, 2); 291 292 ASSERT_EQ(2, kp->poll(pollfds, 2, 0)); 293 // TODO(noelallen) fix poll based on open mode 294 // EXPECT_EQ(0, pollfds[0].revents); 295 // Bug 291018 296 ASSERT_EQ(POLLOUT, pollfds[1].revents); 297 298 CloseFDs(fds, 2); 299 } 300 301 TEST_F(SelectPollTest, SelectMemPipe) { 302 int fds[2]; 303 304 // Both FDs for regular files should be read/write but not exception. 305 fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); 306 fds[1] = kp->open("/test.txt", O_RDONLY); 307 ASSERT_GT(fds[0], -1); 308 ASSERT_GT(fds[1], -1); 309 SetFDs(fds, 2); 310 311 ASSERT_EQ(4, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); 312 EXPECT_NE(0, FD_ISSET(fds[0], &rd_set)); 313 EXPECT_NE(0, FD_ISSET(fds[1], &rd_set)); 314 EXPECT_NE(0, FD_ISSET(fds[0], &wr_set)); 315 EXPECT_NE(0, FD_ISSET(fds[1], &wr_set)); 316 EXPECT_EQ(0, FD_ISSET(fds[0], &ex_set)); 317 EXPECT_EQ(0, FD_ISSET(fds[1], &ex_set)); 318 319 CloseFDs(fds, 2); 320 321 // The write FD should select for write-only, read FD should not select 322 ASSERT_EQ(0, kp->pipe(fds)); 323 SetFDs(fds, 2); 324 325 ASSERT_EQ(2, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); 326 EXPECT_EQ(0, FD_ISSET(fds[0], &rd_set)); 327 EXPECT_EQ(0, FD_ISSET(fds[1], &rd_set)); 328 // TODO(noelallen) fix poll based on open mode 329 // EXPECT_EQ(0, FD_ISSET(fds[0], &wr_set)); 330 // Bug 291018 331 EXPECT_NE(0, FD_ISSET(fds[1], &wr_set)); 332 EXPECT_EQ(0, FD_ISSET(fds[0], &ex_set)); 333 EXPECT_EQ(0, FD_ISSET(fds[1], &ex_set)); 334 } 335 336 /** 337 * Test that calling select() only writes the initial parts of the fd_sets 338 * passed in. 339 * We had an issue when select() was calling FD_ZERO() on the incoming fd_sets 340 * which was causing corruption in ssh which always allocates the fd_sets to be 341 * hold 'nfds' worth of descriptors. 342 */ 343 TEST_F(SelectPollTest, SelectPartialFdset) { 344 int fds[2]; 345 346 // Both FDs for regular files should be read/write but not exception. 347 fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); 348 fds[1] = kp->open("/test.txt", O_RDONLY); 349 ASSERT_GT(fds[0], -1); 350 ASSERT_GT(fds[1], -1); 351 ASSERT_LT(fds[1], 8); 352 SetFDs(fds, 2); 353 354 // Fill in all the remaining bytes in the fd_sets a constant value 355 static const char guard_value = 0xab; 356 for (int i = 1; i < sizeof(fd_set); i++) { 357 ((char*)&rd_set)[i] = guard_value; 358 ((char*)&wr_set)[i] = guard_value; 359 ((char*)&ex_set)[i] = guard_value; 360 } 361 362 ASSERT_EQ(4, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); 363 364 // Verify guard values were not touched. 365 for (int i = 1; i < sizeof(fd_set); i++) { 366 ASSERT_EQ(guard_value, ((char*)&rd_set)[i]); 367 ASSERT_EQ(guard_value, ((char*)&wr_set)[i]); 368 ASSERT_EQ(guard_value, ((char*)&ex_set)[i]); 369 } 370 } 371