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