event_test.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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  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
123const int NUM_THREADS = 10;
124TEST_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
166TEST(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
178TEST(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
212class StreamFsForTesting : public StreamFs {
213 public:
214  StreamFsForTesting() {}
215};
216
217TEST(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
226const int MAX_FDS = 32;
227class 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
272TEST_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, 0777);
277  fds[1] = kp->open("/test.txt", O_RDONLY, 0);
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
301TEST_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, 0777);
306  fds[1] = kp->open("/test.txt", O_RDONLY, 0);
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 */
343TEST_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, 0777);
348  fds[1] = kp->open("/test.txt", O_RDONLY, 0);
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