event_test.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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 28using namespace nacl_io; 29using namespace sdk_util; 30 31class 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 46TEST(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 73class 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 void CreateThread() { 84 pthread_t id; 85 EXPECT_EQ(0, pthread_create(&id, NULL, ThreadThunk, this)); 86 } 87 88 static void* ThreadThunk(void* ptr) { 89 return static_cast<EmitterTest*>(ptr)->ThreadEntry(); 90 } 91 92 void* ThreadEntry() { 93 EventListenerLock listener(&emitter_); 94 95 pthread_cond_signal(&multi_cond_); 96 waiting_++; 97 EXPECT_EQ(0, listener.WaitOnEvent(POLLIN, -1)); 98 emitter_.ClearEvents_Locked(POLLIN); 99 signaled_++; 100 return NULL; 101 } 102 103 protected: 104 pthread_cond_t multi_cond_; 105 EventEmitter emitter_; 106 107 uint32_t waiting_; 108 uint32_t signaled_; 109}; 110 111const int NUM_THREADS = 10; 112TEST_F(EmitterTest, MultiThread) { 113 for (int a = 0; a < NUM_THREADS; a++) 114 CreateThread(); 115 116 { 117 AUTO_LOCK(emitter_.GetLock()); 118 119 // Wait for all threads to wait 120 while (waiting_ < NUM_THREADS) 121 pthread_cond_wait(&multi_cond_, emitter_.GetLock().mutex()); 122 123 ASSERT_EQ(0, signaled_); 124 125 emitter_.RaiseEvents_Locked(POLLIN); 126 } 127 128 // sleep for 50 milliseconds 129 struct timespec sleeptime = {0, 50 * 1000 * 1000}; 130 nanosleep(&sleeptime, NULL); 131 132 EXPECT_EQ(1, signaled_); 133 134 { 135 AUTO_LOCK(emitter_.GetLock()); 136 emitter_.RaiseEvents_Locked(POLLIN); 137 } 138 139 nanosleep(&sleeptime, NULL); 140 EXPECT_EQ(2, signaled_); 141 142 // Clean up remaining threads. 143 while (signaled_ < waiting_) { 144 AUTO_LOCK(emitter_.GetLock()); 145 emitter_.RaiseEvents_Locked(POLLIN); 146 } 147} 148 149TEST(EventListenerPollTest, WaitForAny) { 150 ScopedEventEmitter emitter1(new EventEmitter()); 151 ScopedEventEmitter emitter2(new EventEmitter()); 152 ScopedEventEmitter emitter3(new EventEmitter()); 153 EventListenerPoll listener; 154 EventRequest requests[3] = { 155 {emitter1, 0, 0}, {emitter2, 0, 0}, {emitter3, 0, 0}, }; 156 Error error = 157 listener.WaitOnAny(requests, sizeof(requests) / sizeof(requests[0]), 1); 158 ASSERT_EQ(ETIMEDOUT, error); 159} 160 161TEST(PipeTest, Listener) { 162 const char hello[] = "Hello World."; 163 char tmp[64] = "Goodbye"; 164 165 PipeEventEmitter pipe(32); 166 167 // Expect to time out on input. 168 { 169 EventListenerLock locker(&pipe); 170 EXPECT_EQ(ETIMEDOUT, locker.WaitOnEvent(POLLIN, 0)); 171 } 172 173 // Output should be ready to go. 174 { 175 EventListenerLock locker(&pipe); 176 EXPECT_EQ(0, locker.WaitOnEvent(POLLOUT, 0)); 177 EXPECT_EQ(sizeof(hello), pipe.Write_Locked(hello, sizeof(hello))); 178 } 179 180 // We should now be able to poll 181 { 182 EventListenerLock locker(&pipe); 183 EXPECT_EQ(0, locker.WaitOnEvent(POLLIN, 0)); 184 EXPECT_EQ(sizeof(hello), pipe.Read_Locked(tmp, sizeof(tmp))); 185 } 186 187 // Verify we can read it correctly. 188 EXPECT_EQ(0, strcmp(hello, tmp)); 189} 190 191class StreamFsForTesting : public StreamFs { 192 public: 193 StreamFsForTesting() {} 194}; 195 196TEST(PipeNodeTest, Basic) { 197 ScopedFilesystem fs(new StreamFsForTesting()); 198 199 PipeNode* pipe_node = new PipeNode(fs.get()); 200 ScopedRef<PipeNode> pipe(pipe_node); 201 202 EXPECT_EQ(POLLOUT, pipe_node->GetEventStatus()); 203} 204 205const int MAX_FDS = 32; 206class SelectPollTest : public ::testing::Test { 207 public: 208 void SetUp() { 209 kp = new KernelProxy(); 210 kp->Init(NULL); 211 EXPECT_EQ(0, kp->umount("/")); 212 EXPECT_EQ(0, kp->mount("", "/", "memfs", 0, NULL)); 213 214 memset(&tv, 0, sizeof(tv)); 215 } 216 217 void TearDown() { delete kp; } 218 219 void SetFDs(int* fds, int cnt) { 220 FD_ZERO(&rd_set); 221 FD_ZERO(&wr_set); 222 FD_ZERO(&ex_set); 223 224 for (int index = 0; index < cnt; index++) { 225 EXPECT_NE(-1, fds[index]); 226 FD_SET(fds[index], &rd_set); 227 FD_SET(fds[index], &wr_set); 228 FD_SET(fds[index], &ex_set); 229 230 pollfds[index].fd = fds[index]; 231 pollfds[index].events = POLLIN | POLLOUT; 232 pollfds[index].revents = -1; 233 } 234 } 235 236 void CloseFDs(int* fds, int cnt) { 237 for (int index = 0; index < cnt; index++) 238 kp->close(fds[index]); 239 } 240 241 protected: 242 KernelProxy* kp; 243 244 timeval tv; 245 fd_set rd_set; 246 fd_set wr_set; 247 fd_set ex_set; 248 struct pollfd pollfds[MAX_FDS]; 249}; 250 251TEST_F(SelectPollTest, PollMemPipe) { 252 int fds[2]; 253 254 // Both FDs for regular files should be read/write but not exception. 255 fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); 256 fds[1] = kp->open("/test.txt", O_RDONLY); 257 ASSERT_GT(fds[0], -1); 258 ASSERT_GT(fds[1], -1); 259 260 SetFDs(fds, 2); 261 262 ASSERT_EQ(2, kp->poll(pollfds, 2, 0)); 263 ASSERT_EQ(POLLIN | POLLOUT, pollfds[0].revents); 264 ASSERT_EQ(POLLIN | POLLOUT, pollfds[1].revents); 265 CloseFDs(fds, 2); 266 267 // The write FD should select for write-only, read FD should not select 268 ASSERT_EQ(0, kp->pipe(fds)); 269 SetFDs(fds, 2); 270 271 ASSERT_EQ(2, kp->poll(pollfds, 2, 0)); 272 // TODO(noelallen) fix poll based on open mode 273 // EXPECT_EQ(0, pollfds[0].revents); 274 // Bug 291018 275 ASSERT_EQ(POLLOUT, pollfds[1].revents); 276 277 CloseFDs(fds, 2); 278} 279 280TEST_F(SelectPollTest, SelectMemPipe) { 281 int fds[2]; 282 283 // Both FDs for regular files should be read/write but not exception. 284 fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); 285 fds[1] = kp->open("/test.txt", O_RDONLY); 286 ASSERT_GT(fds[0], -1); 287 ASSERT_GT(fds[1], -1); 288 SetFDs(fds, 2); 289 290 ASSERT_EQ(4, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); 291 EXPECT_NE(0, FD_ISSET(fds[0], &rd_set)); 292 EXPECT_NE(0, FD_ISSET(fds[1], &rd_set)); 293 EXPECT_NE(0, FD_ISSET(fds[0], &wr_set)); 294 EXPECT_NE(0, FD_ISSET(fds[1], &wr_set)); 295 EXPECT_EQ(0, FD_ISSET(fds[0], &ex_set)); 296 EXPECT_EQ(0, FD_ISSET(fds[1], &ex_set)); 297 298 CloseFDs(fds, 2); 299 300 // The write FD should select for write-only, read FD should not select 301 ASSERT_EQ(0, kp->pipe(fds)); 302 SetFDs(fds, 2); 303 304 ASSERT_EQ(2, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); 305 EXPECT_EQ(0, FD_ISSET(fds[0], &rd_set)); 306 EXPECT_EQ(0, FD_ISSET(fds[1], &rd_set)); 307 // TODO(noelallen) fix poll based on open mode 308 // EXPECT_EQ(0, FD_ISSET(fds[0], &wr_set)); 309 // Bug 291018 310 EXPECT_NE(0, FD_ISSET(fds[1], &wr_set)); 311 EXPECT_EQ(0, FD_ISSET(fds[0], &ex_set)); 312 EXPECT_EQ(0, FD_ISSET(fds[1], &ex_set)); 313} 314 315/** 316 * Test that calling select() only writes the initial parts of the fd_sets 317 * passed in. 318 * We had an issue when select() was calling FD_ZERO() on the incoming fd_sets 319 * which was causing corruption in ssh which always allocates the fd_sets to be 320 * hold 'nfds' worth of descriptors. 321 */ 322TEST_F(SelectPollTest, SelectPartialFdset) { 323 int fds[2]; 324 325 // Both FDs for regular files should be read/write but not exception. 326 fds[0] = kp->open("/test.txt", O_CREAT | O_WRONLY); 327 fds[1] = kp->open("/test.txt", O_RDONLY); 328 ASSERT_GT(fds[0], -1); 329 ASSERT_GT(fds[1], -1); 330 ASSERT_LT(fds[1], 8); 331 SetFDs(fds, 2); 332 333 // Fill in all the remaining bytes in the fd_sets a constant value 334 static const char guard_value = 0xab; 335 for (int i = 1; i < sizeof(fd_set); i++) { 336 ((char*)&rd_set)[i] = guard_value; 337 ((char*)&wr_set)[i] = guard_value; 338 ((char*)&ex_set)[i] = guard_value; 339 } 340 341 ASSERT_EQ(4, kp->select(fds[1] + 1, &rd_set, &wr_set, &ex_set, &tv)); 342 343 // Verify guard values were not touched. 344 for (int i = 1; i < sizeof(fd_set); i++) { 345 ASSERT_EQ(guard_value, ((char*)&rd_set)[i]); 346 ASSERT_EQ(guard_value, ((char*)&wr_set)[i]); 347 ASSERT_EQ(guard_value, ((char*)&ex_set)[i]); 348 } 349} 350