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