Mercurial > hg > sv-dependency-builds
comparison src/capnproto-0.6.0/c++/src/kj/async-unix-test.c++ @ 147:45360b968bf4
Cap'n Proto v0.6 + build for OSX
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Mon, 22 May 2017 10:01:37 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
146:206f0eb279b8 | 147:45360b968bf4 |
---|---|
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors | |
2 // Licensed under the MIT License: | |
3 // | |
4 // Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 // of this software and associated documentation files (the "Software"), to deal | |
6 // in the Software without restriction, including without limitation the rights | |
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 // copies of the Software, and to permit persons to whom the Software is | |
9 // furnished to do so, subject to the following conditions: | |
10 // | |
11 // The above copyright notice and this permission notice shall be included in | |
12 // all copies or substantial portions of the Software. | |
13 // | |
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 // THE SOFTWARE. | |
21 | |
22 #if !_WIN32 | |
23 | |
24 #include "async-unix.h" | |
25 #include "thread.h" | |
26 #include "debug.h" | |
27 #include "io.h" | |
28 #include <unistd.h> | |
29 #include <fcntl.h> | |
30 #include <sys/types.h> | |
31 #include <sys/socket.h> | |
32 #include <sys/stat.h> | |
33 #include <netinet/in.h> | |
34 #include <kj/compat/gtest.h> | |
35 #include <pthread.h> | |
36 #include <algorithm> | |
37 | |
38 namespace kj { | |
39 namespace { | |
40 | |
41 inline void delay() { usleep(10000); } | |
42 | |
43 // On OSX, si_code seems to be zero when SI_USER is expected. | |
44 #if __linux__ || __CYGWIN__ | |
45 #define EXPECT_SI_CODE EXPECT_EQ | |
46 #else | |
47 #define EXPECT_SI_CODE(a,b) | |
48 #endif | |
49 | |
50 void captureSignals() { | |
51 static bool captured = false; | |
52 if (!captured) { | |
53 captured = true; | |
54 | |
55 // We use SIGIO and SIGURG as our test signals because they're two signals that we can be | |
56 // reasonably confident won't otherwise be delivered to any KJ or Cap'n Proto test. We can't | |
57 // use SIGUSR1 because it is reserved by UnixEventPort and SIGUSR2 is used by Valgrind on OSX. | |
58 UnixEventPort::captureSignal(SIGURG); | |
59 UnixEventPort::captureSignal(SIGIO); | |
60 } | |
61 } | |
62 | |
63 TEST(AsyncUnixTest, Signals) { | |
64 captureSignals(); | |
65 UnixEventPort port; | |
66 EventLoop loop(port); | |
67 WaitScope waitScope(loop); | |
68 | |
69 kill(getpid(), SIGURG); | |
70 | |
71 siginfo_t info = port.onSignal(SIGURG).wait(waitScope); | |
72 EXPECT_EQ(SIGURG, info.si_signo); | |
73 EXPECT_SI_CODE(SI_USER, info.si_code); | |
74 } | |
75 | |
76 #if defined(SIGRTMIN) && !__BIONIC__ && !(__linux__ && __mips__) | |
77 TEST(AsyncUnixTest, SignalWithValue) { | |
78 // This tests that if we use sigqueue() to attach a value to the signal, that value is received | |
79 // correctly. Note that this only works on platforms that support real-time signals -- even | |
80 // though the signal we're sending is SIGURG, the sigqueue() system call is introduced by RT | |
81 // signals. Hence this test won't run on e.g. Mac OSX. | |
82 // | |
83 // Also, Android's bionic does not appear to support sigqueue() even though the kernel does. | |
84 // | |
85 // Also, this test fails on Linux on mipsel. si_value comes back as zero. No one with a mips | |
86 // machine wants to debug the problem but they demand a patch fixing it, so we disable the test. | |
87 // Sad. https://github.com/sandstorm-io/capnproto/issues/204 | |
88 | |
89 captureSignals(); | |
90 UnixEventPort port; | |
91 EventLoop loop(port); | |
92 WaitScope waitScope(loop); | |
93 | |
94 union sigval value; | |
95 memset(&value, 0, sizeof(value)); | |
96 value.sival_int = 123; | |
97 sigqueue(getpid(), SIGURG, value); | |
98 | |
99 siginfo_t info = port.onSignal(SIGURG).wait(waitScope); | |
100 EXPECT_EQ(SIGURG, info.si_signo); | |
101 EXPECT_SI_CODE(SI_QUEUE, info.si_code); | |
102 EXPECT_EQ(123, info.si_value.sival_int); | |
103 } | |
104 | |
105 TEST(AsyncUnixTest, SignalWithPointerValue) { | |
106 // This tests that if we use sigqueue() to attach a value to the signal, that value is received | |
107 // correctly. Note that this only works on platforms that support real-time signals -- even | |
108 // though the signal we're sending is SIGURG, the sigqueue() system call is introduced by RT | |
109 // signals. Hence this test won't run on e.g. Mac OSX. | |
110 // | |
111 // Also, Android's bionic does not appear to support sigqueue() even though the kernel does. | |
112 // | |
113 // Also, this test fails on Linux on mipsel. si_value comes back as zero. No one with a mips | |
114 // machine wants to debug the problem but they demand a patch fixing it, so we disable the test. | |
115 // Sad. https://github.com/sandstorm-io/capnproto/issues/204 | |
116 | |
117 captureSignals(); | |
118 UnixEventPort port; | |
119 EventLoop loop(port); | |
120 WaitScope waitScope(loop); | |
121 | |
122 union sigval value; | |
123 memset(&value, 0, sizeof(value)); | |
124 value.sival_ptr = &port; | |
125 sigqueue(getpid(), SIGURG, value); | |
126 | |
127 siginfo_t info = port.onSignal(SIGURG).wait(waitScope); | |
128 EXPECT_EQ(SIGURG, info.si_signo); | |
129 EXPECT_SI_CODE(SI_QUEUE, info.si_code); | |
130 EXPECT_EQ(&port, info.si_value.sival_ptr); | |
131 } | |
132 #endif | |
133 | |
134 TEST(AsyncUnixTest, SignalsMultiListen) { | |
135 captureSignals(); | |
136 UnixEventPort port; | |
137 EventLoop loop(port); | |
138 WaitScope waitScope(loop); | |
139 | |
140 port.onSignal(SIGIO).then([](siginfo_t&&) { | |
141 KJ_FAIL_EXPECT("Received wrong signal."); | |
142 }).detach([](kj::Exception&& exception) { | |
143 KJ_FAIL_EXPECT(exception); | |
144 }); | |
145 | |
146 kill(getpid(), SIGURG); | |
147 | |
148 siginfo_t info = port.onSignal(SIGURG).wait(waitScope); | |
149 EXPECT_EQ(SIGURG, info.si_signo); | |
150 EXPECT_SI_CODE(SI_USER, info.si_code); | |
151 } | |
152 | |
153 #if !__CYGWIN32__ | |
154 // Cygwin32 (but not Cygwin64) appears not to deliver SIGURG in the following test (but it does | |
155 // deliver SIGIO, if you reverse the order of the waits). Since this doesn't occur on any other | |
156 // platform I'm assuming it's a Cygwin bug. | |
157 | |
158 TEST(AsyncUnixTest, SignalsMultiReceive) { | |
159 captureSignals(); | |
160 UnixEventPort port; | |
161 EventLoop loop(port); | |
162 WaitScope waitScope(loop); | |
163 | |
164 kill(getpid(), SIGURG); | |
165 kill(getpid(), SIGIO); | |
166 | |
167 siginfo_t info = port.onSignal(SIGURG).wait(waitScope); | |
168 EXPECT_EQ(SIGURG, info.si_signo); | |
169 EXPECT_SI_CODE(SI_USER, info.si_code); | |
170 | |
171 info = port.onSignal(SIGIO).wait(waitScope); | |
172 EXPECT_EQ(SIGIO, info.si_signo); | |
173 EXPECT_SI_CODE(SI_USER, info.si_code); | |
174 } | |
175 | |
176 #endif // !__CYGWIN32__ | |
177 | |
178 TEST(AsyncUnixTest, SignalsAsync) { | |
179 captureSignals(); | |
180 UnixEventPort port; | |
181 EventLoop loop(port); | |
182 WaitScope waitScope(loop); | |
183 | |
184 // Arrange for a signal to be sent from another thread. | |
185 pthread_t mainThread = pthread_self(); | |
186 Thread thread([&]() { | |
187 delay(); | |
188 pthread_kill(mainThread, SIGURG); | |
189 }); | |
190 | |
191 siginfo_t info = port.onSignal(SIGURG).wait(waitScope); | |
192 EXPECT_EQ(SIGURG, info.si_signo); | |
193 #if __linux__ | |
194 EXPECT_SI_CODE(SI_TKILL, info.si_code); | |
195 #endif | |
196 } | |
197 | |
198 #if !__CYGWIN32__ | |
199 // Cygwin32 (but not Cygwin64) appears not to deliver SIGURG in the following test (but it does | |
200 // deliver SIGIO, if you reverse the order of the waits). Since this doesn't occur on any other | |
201 // platform I'm assuming it's a Cygwin bug. | |
202 | |
203 TEST(AsyncUnixTest, SignalsNoWait) { | |
204 // Verify that UnixEventPort::poll() correctly receives pending signals. | |
205 | |
206 captureSignals(); | |
207 UnixEventPort port; | |
208 EventLoop loop(port); | |
209 WaitScope waitScope(loop); | |
210 | |
211 bool receivedSigurg = false; | |
212 bool receivedSigio = false; | |
213 port.onSignal(SIGURG).then([&](siginfo_t&& info) { | |
214 receivedSigurg = true; | |
215 EXPECT_EQ(SIGURG, info.si_signo); | |
216 EXPECT_SI_CODE(SI_USER, info.si_code); | |
217 }).detach([](Exception&& e) { KJ_FAIL_EXPECT(e); }); | |
218 port.onSignal(SIGIO).then([&](siginfo_t&& info) { | |
219 receivedSigio = true; | |
220 EXPECT_EQ(SIGIO, info.si_signo); | |
221 EXPECT_SI_CODE(SI_USER, info.si_code); | |
222 }).detach([](Exception&& e) { KJ_FAIL_EXPECT(e); }); | |
223 | |
224 kill(getpid(), SIGURG); | |
225 kill(getpid(), SIGIO); | |
226 | |
227 EXPECT_FALSE(receivedSigurg); | |
228 EXPECT_FALSE(receivedSigio); | |
229 | |
230 loop.run(); | |
231 | |
232 EXPECT_FALSE(receivedSigurg); | |
233 EXPECT_FALSE(receivedSigio); | |
234 | |
235 port.poll(); | |
236 | |
237 EXPECT_FALSE(receivedSigurg); | |
238 EXPECT_FALSE(receivedSigio); | |
239 | |
240 loop.run(); | |
241 | |
242 EXPECT_TRUE(receivedSigurg); | |
243 EXPECT_TRUE(receivedSigio); | |
244 } | |
245 | |
246 #endif // !__CYGWIN32__ | |
247 | |
248 TEST(AsyncUnixTest, ReadObserver) { | |
249 captureSignals(); | |
250 UnixEventPort port; | |
251 EventLoop loop(port); | |
252 WaitScope waitScope(loop); | |
253 | |
254 int pipefds[2]; | |
255 KJ_SYSCALL(pipe(pipefds)); | |
256 kj::AutoCloseFd infd(pipefds[0]), outfd(pipefds[1]); | |
257 | |
258 UnixEventPort::FdObserver observer(port, infd, UnixEventPort::FdObserver::OBSERVE_READ); | |
259 | |
260 KJ_SYSCALL(write(outfd, "foo", 3)); | |
261 | |
262 observer.whenBecomesReadable().wait(waitScope); | |
263 | |
264 #if __linux__ // platform known to support POLLRDHUP | |
265 EXPECT_FALSE(KJ_ASSERT_NONNULL(observer.atEndHint())); | |
266 | |
267 char buffer[4096]; | |
268 ssize_t n; | |
269 KJ_SYSCALL(n = read(infd, &buffer, sizeof(buffer))); | |
270 EXPECT_EQ(3, n); | |
271 | |
272 KJ_SYSCALL(write(outfd, "bar", 3)); | |
273 outfd = nullptr; | |
274 | |
275 observer.whenBecomesReadable().wait(waitScope); | |
276 | |
277 EXPECT_TRUE(KJ_ASSERT_NONNULL(observer.atEndHint())); | |
278 #endif | |
279 } | |
280 | |
281 TEST(AsyncUnixTest, ReadObserverMultiListen) { | |
282 captureSignals(); | |
283 UnixEventPort port; | |
284 EventLoop loop(port); | |
285 WaitScope waitScope(loop); | |
286 | |
287 int bogusPipefds[2]; | |
288 KJ_SYSCALL(pipe(bogusPipefds)); | |
289 KJ_DEFER({ close(bogusPipefds[1]); close(bogusPipefds[0]); }); | |
290 | |
291 UnixEventPort::FdObserver bogusObserver(port, bogusPipefds[0], | |
292 UnixEventPort::FdObserver::OBSERVE_READ); | |
293 | |
294 bogusObserver.whenBecomesReadable().then([]() { | |
295 ADD_FAILURE() << "Received wrong poll."; | |
296 }).detach([](kj::Exception&& exception) { | |
297 ADD_FAILURE() << kj::str(exception).cStr(); | |
298 }); | |
299 | |
300 int pipefds[2]; | |
301 KJ_SYSCALL(pipe(pipefds)); | |
302 KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); | |
303 | |
304 UnixEventPort::FdObserver observer(port, pipefds[0], | |
305 UnixEventPort::FdObserver::OBSERVE_READ); | |
306 KJ_SYSCALL(write(pipefds[1], "foo", 3)); | |
307 | |
308 observer.whenBecomesReadable().wait(waitScope); | |
309 } | |
310 | |
311 TEST(AsyncUnixTest, ReadObserverMultiReceive) { | |
312 captureSignals(); | |
313 UnixEventPort port; | |
314 EventLoop loop(port); | |
315 WaitScope waitScope(loop); | |
316 | |
317 int pipefds[2]; | |
318 KJ_SYSCALL(pipe(pipefds)); | |
319 KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); | |
320 | |
321 UnixEventPort::FdObserver observer(port, pipefds[0], | |
322 UnixEventPort::FdObserver::OBSERVE_READ); | |
323 KJ_SYSCALL(write(pipefds[1], "foo", 3)); | |
324 | |
325 int pipefds2[2]; | |
326 KJ_SYSCALL(pipe(pipefds2)); | |
327 KJ_DEFER({ close(pipefds2[1]); close(pipefds2[0]); }); | |
328 | |
329 UnixEventPort::FdObserver observer2(port, pipefds2[0], | |
330 UnixEventPort::FdObserver::OBSERVE_READ); | |
331 KJ_SYSCALL(write(pipefds2[1], "bar", 3)); | |
332 | |
333 auto promise1 = observer.whenBecomesReadable(); | |
334 auto promise2 = observer2.whenBecomesReadable(); | |
335 promise1.wait(waitScope); | |
336 promise2.wait(waitScope); | |
337 } | |
338 | |
339 TEST(AsyncUnixTest, ReadObserverAsync) { | |
340 captureSignals(); | |
341 UnixEventPort port; | |
342 EventLoop loop(port); | |
343 WaitScope waitScope(loop); | |
344 | |
345 // Make a pipe and wait on its read end while another thread writes to it. | |
346 int pipefds[2]; | |
347 KJ_SYSCALL(pipe(pipefds)); | |
348 KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); | |
349 UnixEventPort::FdObserver observer(port, pipefds[0], | |
350 UnixEventPort::FdObserver::OBSERVE_READ); | |
351 | |
352 Thread thread([&]() { | |
353 delay(); | |
354 KJ_SYSCALL(write(pipefds[1], "foo", 3)); | |
355 }); | |
356 | |
357 // Wait for the event in this thread. | |
358 observer.whenBecomesReadable().wait(waitScope); | |
359 } | |
360 | |
361 TEST(AsyncUnixTest, ReadObserverNoWait) { | |
362 // Verify that UnixEventPort::poll() correctly receives pending FD events. | |
363 | |
364 captureSignals(); | |
365 UnixEventPort port; | |
366 EventLoop loop(port); | |
367 WaitScope waitScope(loop); | |
368 | |
369 int pipefds[2]; | |
370 KJ_SYSCALL(pipe(pipefds)); | |
371 KJ_DEFER({ close(pipefds[1]); close(pipefds[0]); }); | |
372 UnixEventPort::FdObserver observer(port, pipefds[0], | |
373 UnixEventPort::FdObserver::OBSERVE_READ); | |
374 | |
375 int pipefds2[2]; | |
376 KJ_SYSCALL(pipe(pipefds2)); | |
377 KJ_DEFER({ close(pipefds2[1]); close(pipefds2[0]); }); | |
378 UnixEventPort::FdObserver observer2(port, pipefds2[0], | |
379 UnixEventPort::FdObserver::OBSERVE_READ); | |
380 | |
381 int receivedCount = 0; | |
382 observer.whenBecomesReadable().then([&]() { | |
383 receivedCount++; | |
384 }).detach([](Exception&& e) { ADD_FAILURE() << str(e).cStr(); }); | |
385 observer2.whenBecomesReadable().then([&]() { | |
386 receivedCount++; | |
387 }).detach([](Exception&& e) { ADD_FAILURE() << str(e).cStr(); }); | |
388 | |
389 KJ_SYSCALL(write(pipefds[1], "foo", 3)); | |
390 KJ_SYSCALL(write(pipefds2[1], "bar", 3)); | |
391 | |
392 EXPECT_EQ(0, receivedCount); | |
393 | |
394 loop.run(); | |
395 | |
396 EXPECT_EQ(0, receivedCount); | |
397 | |
398 port.poll(); | |
399 | |
400 EXPECT_EQ(0, receivedCount); | |
401 | |
402 loop.run(); | |
403 | |
404 EXPECT_EQ(2, receivedCount); | |
405 } | |
406 | |
407 static void setNonblocking(int fd) { | |
408 int flags; | |
409 KJ_SYSCALL(flags = fcntl(fd, F_GETFL)); | |
410 if ((flags & O_NONBLOCK) == 0) { | |
411 KJ_SYSCALL(fcntl(fd, F_SETFL, flags | O_NONBLOCK)); | |
412 } | |
413 } | |
414 | |
415 TEST(AsyncUnixTest, WriteObserver) { | |
416 captureSignals(); | |
417 UnixEventPort port; | |
418 EventLoop loop(port); | |
419 WaitScope waitScope(loop); | |
420 | |
421 int pipefds[2]; | |
422 KJ_SYSCALL(pipe(pipefds)); | |
423 kj::AutoCloseFd infd(pipefds[0]), outfd(pipefds[1]); | |
424 setNonblocking(outfd); | |
425 setNonblocking(infd); | |
426 | |
427 UnixEventPort::FdObserver observer(port, outfd, UnixEventPort::FdObserver::OBSERVE_WRITE); | |
428 | |
429 // Fill buffer. | |
430 ssize_t n; | |
431 do { | |
432 KJ_NONBLOCKING_SYSCALL(n = write(outfd, "foo", 3)); | |
433 } while (n >= 0); | |
434 | |
435 bool writable = false; | |
436 auto promise = observer.whenBecomesWritable() | |
437 .then([&]() { writable = true; }).eagerlyEvaluate(nullptr); | |
438 | |
439 loop.run(); | |
440 port.poll(); | |
441 loop.run(); | |
442 | |
443 EXPECT_FALSE(writable); | |
444 | |
445 // Empty the read end so that the write end becomes writable. Note that Linux implements a | |
446 // high watermark / low watermark heuristic which means that only reading one byte is not | |
447 // sufficient. The amount we have to read is in fact architecture-dependent -- it appears to be | |
448 // 1 page. To be safe, we read everything. | |
449 char buffer[4096]; | |
450 do { | |
451 KJ_NONBLOCKING_SYSCALL(n = read(infd, &buffer, sizeof(buffer))); | |
452 } while (n > 0); | |
453 | |
454 loop.run(); | |
455 port.poll(); | |
456 loop.run(); | |
457 | |
458 EXPECT_TRUE(writable); | |
459 } | |
460 | |
461 #if !__APPLE__ | |
462 // Disabled on macOS due to https://github.com/sandstorm-io/capnproto/issues/374. | |
463 TEST(AsyncUnixTest, UrgentObserver) { | |
464 // Verify that FdObserver correctly detects availability of out-of-band data. | |
465 // Availability of out-of-band data is implementation-specific. | |
466 // Linux's and OS X's TCP/IP stack supports out-of-band messages for TCP sockets, which is used | |
467 // for this test. | |
468 | |
469 UnixEventPort port; | |
470 EventLoop loop(port); | |
471 WaitScope waitScope(loop); | |
472 int tmpFd; | |
473 char c; | |
474 | |
475 // Spawn a TCP server | |
476 KJ_SYSCALL(tmpFd = socket(AF_INET, SOCK_STREAM, 0)); | |
477 kj::AutoCloseFd serverFd(tmpFd); | |
478 sockaddr_in saddr; | |
479 memset(&saddr, 0, sizeof(saddr)); | |
480 saddr.sin_family = AF_INET; | |
481 saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
482 KJ_SYSCALL(bind(serverFd, reinterpret_cast<sockaddr*>(&saddr), sizeof(saddr))); | |
483 socklen_t saddrLen = sizeof(saddr); | |
484 KJ_SYSCALL(getsockname(serverFd, reinterpret_cast<sockaddr*>(&saddr), &saddrLen)); | |
485 KJ_SYSCALL(listen(serverFd, 1)); | |
486 | |
487 // Accept one connection, send in-band and OOB byte, wait for a quit message | |
488 Thread thread([&]() { | |
489 int tmpFd; | |
490 char c; | |
491 | |
492 sockaddr_in caddr; | |
493 socklen_t caddrLen = sizeof(caddr); | |
494 KJ_SYSCALL(tmpFd = accept(serverFd, reinterpret_cast<sockaddr*>(&caddr), &caddrLen)); | |
495 kj::AutoCloseFd clientFd(tmpFd); | |
496 delay(); | |
497 | |
498 // Workaround: OS X won't signal POLLPRI without POLLIN. Also enqueue some in-band data. | |
499 c = 'i'; | |
500 KJ_SYSCALL(send(clientFd, &c, 1, 0)); | |
501 c = 'o'; | |
502 KJ_SYSCALL(send(clientFd, &c, 1, MSG_OOB)); | |
503 | |
504 KJ_SYSCALL(recv(clientFd, &c, 1, 0)); | |
505 EXPECT_EQ('q', c); | |
506 }); | |
507 KJ_DEFER({ shutdown(serverFd, SHUT_RDWR); serverFd = nullptr; }); | |
508 | |
509 KJ_SYSCALL(tmpFd = socket(AF_INET, SOCK_STREAM, 0)); | |
510 kj::AutoCloseFd clientFd(tmpFd); | |
511 KJ_SYSCALL(connect(clientFd, reinterpret_cast<sockaddr*>(&saddr), saddrLen)); | |
512 | |
513 UnixEventPort::FdObserver observer(port, clientFd, | |
514 UnixEventPort::FdObserver::OBSERVE_READ | UnixEventPort::FdObserver::OBSERVE_URGENT); | |
515 | |
516 observer.whenUrgentDataAvailable().wait(waitScope); | |
517 | |
518 #if __CYGWIN__ | |
519 // On Cygwin, reading the urgent byte first causes the subsequent regular read to block until | |
520 // such a time as the connection closes -- and then the byte is successfully returned. This | |
521 // seems to be a cygwin bug. | |
522 KJ_SYSCALL(recv(clientFd, &c, 1, 0)); | |
523 EXPECT_EQ('i', c); | |
524 KJ_SYSCALL(recv(clientFd, &c, 1, MSG_OOB)); | |
525 EXPECT_EQ('o', c); | |
526 #else | |
527 // Attempt to read the urgent byte prior to reading the in-band byte. | |
528 KJ_SYSCALL(recv(clientFd, &c, 1, MSG_OOB)); | |
529 EXPECT_EQ('o', c); | |
530 KJ_SYSCALL(recv(clientFd, &c, 1, 0)); | |
531 EXPECT_EQ('i', c); | |
532 #endif | |
533 | |
534 // Allow server thread to let its clientFd go out of scope. | |
535 c = 'q'; | |
536 KJ_SYSCALL(send(clientFd, &c, 1, 0)); | |
537 KJ_SYSCALL(shutdown(clientFd, SHUT_RDWR)); | |
538 } | |
539 #endif | |
540 | |
541 TEST(AsyncUnixTest, SteadyTimers) { | |
542 captureSignals(); | |
543 UnixEventPort port; | |
544 EventLoop loop(port); | |
545 WaitScope waitScope(loop); | |
546 | |
547 auto& timer = port.getTimer(); | |
548 | |
549 auto start = timer.now(); | |
550 kj::Vector<TimePoint> expected; | |
551 kj::Vector<TimePoint> actual; | |
552 | |
553 auto addTimer = [&](Duration delay) { | |
554 expected.add(max(start + delay, start)); | |
555 timer.atTime(start + delay).then([&]() { | |
556 actual.add(timer.now()); | |
557 }).detach([](Exception&& e) { ADD_FAILURE() << str(e).cStr(); }); | |
558 }; | |
559 | |
560 addTimer(30 * MILLISECONDS); | |
561 addTimer(40 * MILLISECONDS); | |
562 addTimer(20350 * MICROSECONDS); | |
563 addTimer(30 * MILLISECONDS); | |
564 addTimer(-10 * MILLISECONDS); | |
565 | |
566 std::sort(expected.begin(), expected.end()); | |
567 timer.atTime(expected.back() + MILLISECONDS).wait(waitScope); | |
568 | |
569 ASSERT_EQ(expected.size(), actual.size()); | |
570 for (int i = 0; i < expected.size(); ++i) { | |
571 KJ_EXPECT(expected[i] <= actual[i], "Actual time for timer i is too early.", | |
572 i, ((expected[i] - actual[i]) / NANOSECONDS)); | |
573 } | |
574 } | |
575 | |
576 TEST(AsyncUnixTest, Wake) { | |
577 captureSignals(); | |
578 UnixEventPort port; | |
579 EventLoop loop(port); | |
580 WaitScope waitScope(loop); | |
581 | |
582 EXPECT_FALSE(port.poll()); | |
583 port.wake(); | |
584 EXPECT_TRUE(port.poll()); | |
585 EXPECT_FALSE(port.poll()); | |
586 | |
587 port.wake(); | |
588 EXPECT_TRUE(port.wait()); | |
589 | |
590 { | |
591 auto promise = port.getTimer().atTime(port.getTimer().now()); | |
592 EXPECT_FALSE(port.wait()); | |
593 } | |
594 | |
595 bool woken = false; | |
596 Thread thread([&]() { | |
597 delay(); | |
598 woken = true; | |
599 port.wake(); | |
600 }); | |
601 | |
602 EXPECT_TRUE(port.wait()); | |
603 } | |
604 | |
605 } // namespace | |
606 } // namespace kj | |
607 | |
608 #endif // !_WIN32 |