From 5f066e06b991041f2c1d988f493d5b29f8e56b7e Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 23 Sep 2011 16:13:14 -0700 Subject: [PATCH] Update to libuv commit 3ca382. This patch changes libuv's gyp build system to make it's own makefiles. To generate them for rust, run these commands. They requires python 2.x to work: $ mkdir -p src/rt/libuv/build $ svn co http://gyp.googlecode.com/svn src/rt/libuv/build/gyp $ ./etc/src/gyp_uv --- .gitignore | 2 +- configure | 4 - mk/clean.mk | 1 + mk/libuv/mac/Makefile | 359 ++ mk/libuv/mac/gyp-mac-tool | 188 + .../mac/src/rt/libuv/run-benchmarks.target.mk | 91 + mk/libuv/mac/src/rt/libuv/run-tests.target.mk | 114 + mk/libuv/mac/src/rt/libuv/uv.Makefile | 6 + mk/libuv/mac/src/rt/libuv/uv.target.mk | 141 + mk/libuv/unix/Makefile | 337 ++ .../src/rt/libuv/run-benchmarks.target.mk | 78 + .../unix/src/rt/libuv/run-tests.target.mk | 101 + mk/libuv/unix/src/rt/libuv/uv.Makefile | 6 + mk/libuv/unix/src/rt/libuv/uv.target.mk | 134 + mk/libuv/win/Makefile | 337 ++ .../win/src/rt/libuv/run-benchmarks.target.mk | 79 + mk/libuv/win/src/rt/libuv/run-tests.target.mk | 102 + mk/libuv/win/src/rt/libuv/uv.Makefile | 6 + mk/libuv/win/src/rt/libuv/uv.target.mk | 135 + mk/platform.mk | 2 +- mk/rt.mk | 23 +- src/etc/gyp-uv | 36 + src/rt/libuv/.gitignore | 50 +- src/rt/libuv/.mailmap | 8 + src/rt/libuv/AUTHORS | 17 +- src/rt/libuv/LIBUV_REVISION | 5 - src/rt/libuv/Makefile | 60 +- src/rt/libuv/README | 16 - src/rt/libuv/README.md | 92 + src/rt/libuv/common.gypi | 164 + src/rt/libuv/config-mingw.mk | 44 +- src/rt/libuv/config-unix.mk | 112 +- src/rt/libuv/doc/desired-api.md | 159 - src/rt/libuv/gyp_uv | 60 + src/rt/libuv/include/{ => uv-private}/eio.h | 126 +- src/rt/libuv/include/{ => uv-private}/ev.h | 24 +- .../include/{ => uv-private}/ngx-queue.h | 0 src/rt/libuv/include/{ => uv-private}/tree.h | 0 .../uv-private/uv-linux.h} | 34 +- .../libuv/include/{ => uv-private}/uv-unix.h | 96 +- src/rt/libuv/include/uv-private/uv-win.h | 281 ++ src/rt/libuv/include/uv-win.h | 130 - src/rt/libuv/include/uv.h | 835 +++- src/rt/libuv/msvs/c-ares.vcxproj | 179 - src/rt/libuv/msvs/libuv-benchmark.vcxproj | 167 - src/rt/libuv/msvs/libuv-test.vcxproj | 180 - src/rt/libuv/msvs/libuv.sln | 56 - src/rt/libuv/msvs/libuv.vcxproj | 131 - src/rt/libuv/src/ares/ares_parse_a_reply.c | 2 + .../src/ares/config_netbsd/ares_config.h | 510 ++ src/rt/libuv/src/ares/inet_ntop.h | 1 + src/rt/libuv/src/eio/Changes | 35 - src/rt/libuv/src/eio/autogen.sh | 5 - src/rt/libuv/src/eio/eio.pod | 303 -- src/rt/libuv/src/unix/cares.c | 185 + src/rt/libuv/src/unix/core.c | 799 +++ .../libuv/src/{uv-cygwin.c => unix/cygwin.c} | 16 + .../libuv/src/{uv-darwin.c => unix/darwin.c} | 19 + src/rt/libuv/src/unix/eio/Changes | 63 + src/rt/libuv/src/{ => unix}/eio/LICENSE | 0 src/rt/libuv/src/{ => unix}/eio/Makefile.am | 2 +- src/rt/libuv/src/{ => unix}/eio/aclocal.m4 | 0 src/rt/libuv/src/unix/eio/autogen.sh | 3 + src/rt/libuv/src/{ => unix}/eio/config.h.in | 0 .../libuv/src/{ => unix}/eio/config_cygwin.h | 3 + src/rt/libuv/src/unix/eio/config_darwin.h | 141 + .../libuv/src/{ => unix}/eio/config_freebsd.h | 3 + .../libuv/src/{ => unix}/eio/config_linux.h | 18 +- .../eio/config_netbsd.h} | 13 +- .../libuv/src/{ => unix}/eio/config_sunos.h | 3 + src/rt/libuv/src/{ => unix}/eio/configure.ac | 8 +- src/rt/libuv/src/{ => unix}/eio/demo.c | 0 src/rt/libuv/src/unix/eio/ecb.h | 370 ++ src/rt/libuv/src/{ => unix}/eio/eio.3 | 0 src/rt/libuv/src/{ => unix}/eio/eio.c | 1485 ++++-- src/rt/libuv/src/unix/eio/eio.pod | 969 ++++ src/rt/libuv/src/{ => unix}/eio/libeio.m4 | 39 + src/rt/libuv/src/{ => unix}/eio/xthread.h | 43 +- src/rt/libuv/src/unix/error.c | 110 + src/rt/libuv/src/{ => unix}/ev/Changes | 0 src/rt/libuv/src/{ => unix}/ev/LICENSE | 0 src/rt/libuv/src/{ => unix}/ev/Makefile.am | 0 src/rt/libuv/src/{ => unix}/ev/Makefile.in | 0 src/rt/libuv/src/{ => unix}/ev/README | 0 src/rt/libuv/src/{ => unix}/ev/aclocal.m4 | 0 src/rt/libuv/src/{ => unix}/ev/autogen.sh | 0 src/rt/libuv/src/{ => unix}/ev/config.guess | 0 src/rt/libuv/src/{ => unix}/ev/config.h.in | 0 src/rt/libuv/src/{ => unix}/ev/config.sub | 0 .../libuv/src/{ => unix}/ev/config_cygwin.h | 0 .../libuv/src/{ => unix}/ev/config_darwin.h | 0 .../libuv/src/{ => unix}/ev/config_freebsd.h | 0 src/rt/libuv/src/{ => unix}/ev/config_linux.h | 37 +- src/rt/libuv/src/unix/ev/config_netbsd.h | 120 + src/rt/libuv/src/{ => unix}/ev/config_sunos.h | 0 src/rt/libuv/src/{ => unix}/ev/configure | 0 src/rt/libuv/src/{ => unix}/ev/configure.ac | 0 src/rt/libuv/src/{ => unix}/ev/depcomp | 0 src/rt/libuv/src/{ => unix}/ev/ev++.h | 0 src/rt/libuv/src/{ => unix}/ev/ev.3 | 0 src/rt/libuv/src/{ => unix}/ev/ev.c | 2 +- src/rt/libuv/src/{ => unix}/ev/ev.pod | 0 src/rt/libuv/src/{ => unix}/ev/ev_epoll.c | 0 src/rt/libuv/src/{ => unix}/ev/ev_kqueue.c | 0 src/rt/libuv/src/{ => unix}/ev/ev_poll.c | 0 src/rt/libuv/src/{ => unix}/ev/ev_port.c | 0 src/rt/libuv/src/{ => unix}/ev/ev_select.c | 0 src/rt/libuv/src/{ => unix}/ev/ev_vars.h | 0 src/rt/libuv/src/{ => unix}/ev/ev_win32.c | 0 src/rt/libuv/src/{ => unix}/ev/ev_wrap.h | 0 src/rt/libuv/src/{ => unix}/ev/event.c | 0 src/rt/libuv/src/{ => unix}/ev/event.h | 0 src/rt/libuv/src/{ => unix}/ev/install-sh | 0 src/rt/libuv/src/{ => unix}/ev/libev.m4 | 0 src/rt/libuv/src/{ => unix}/ev/ltmain.sh | 0 src/rt/libuv/src/{ => unix}/ev/missing | 0 src/rt/libuv/src/{ => unix}/ev/mkinstalldirs | 0 .../libuv/src/{uv-linux.c => unix/freebsd.c} | 47 +- src/rt/libuv/src/unix/fs.c | 697 +++ src/rt/libuv/src/unix/internal.h | 129 + src/rt/libuv/src/unix/linux.c | 183 + src/rt/libuv/src/unix/netbsd.c | 85 + src/rt/libuv/src/unix/pipe.c | 272 ++ src/rt/libuv/src/unix/process.c | 300 ++ src/rt/libuv/src/unix/stream.c | 783 +++ src/rt/libuv/src/{uv-sunos.c => unix/sunos.c} | 21 +- src/rt/libuv/src/unix/tcp.c | 280 ++ src/rt/libuv/src/unix/tty.c | 110 + src/rt/libuv/src/unix/udp.c | 555 +++ src/rt/libuv/src/{ => unix}/uv-eio.c | 75 +- src/rt/libuv/src/{ => unix}/uv-eio.h | 2 +- src/rt/libuv/src/uv-common.c | 54 +- src/rt/libuv/src/uv-common.h | 15 +- src/rt/libuv/src/uv-unix.c | 1596 ------ src/rt/libuv/src/uv-win.c | 2455 ---------- src/rt/libuv/src/win/async.c | 127 + src/rt/libuv/src/win/cares.c | 289 ++ src/rt/libuv/src/win/core.c | 218 + src/rt/libuv/src/win/error.c | 157 + src/rt/libuv/src/win/fs-event.c | 384 ++ src/rt/libuv/src/win/fs.c | 1302 +++++ src/rt/libuv/src/win/getaddrinfo.c | 368 ++ src/rt/libuv/src/win/handle.c | 193 + src/rt/libuv/src/win/internal.h | 305 ++ src/rt/libuv/src/win/loop-watcher.c | 131 + src/rt/libuv/src/win/pipe.c | 1067 ++++ src/rt/libuv/src/win/process.c | 1045 ++++ src/rt/libuv/src/win/req.c | 176 + src/rt/libuv/src/win/stdio.c | 75 + src/rt/libuv/src/win/stream.c | 154 + src/rt/libuv/src/win/tcp.c | 870 ++++ src/rt/libuv/src/win/threadpool.c | 73 + src/rt/libuv/src/win/threads.c | 81 + src/rt/libuv/src/win/timer.c | 276 ++ src/rt/libuv/src/win/tty.c | 61 + src/rt/libuv/src/win/udp.c | 598 +++ src/rt/libuv/src/win/util.c | 96 + src/rt/libuv/src/win/winapi.c | 81 + src/rt/libuv/src/win/winapi.h | 4337 +++++++++++++++++ src/rt/libuv/src/win/winsock.c | 270 + src/rt/libuv/src/win/winsock.h | 134 + src/rt/libuv/test/benchmark-ares.c | 20 +- src/rt/libuv/test/benchmark-getaddrinfo.c | 22 +- src/rt/libuv/test/benchmark-list.h | 68 +- src/rt/libuv/test/benchmark-ping-pongs.c | 50 +- src/rt/libuv/test/benchmark-pound.c | 327 ++ src/rt/libuv/test/benchmark-pump.c | 206 +- src/rt/libuv/test/benchmark-sizes.c | 6 +- src/rt/libuv/test/benchmark-spawn.c | 156 + .../libuv/test/benchmark-udp-packet-storm.c | 249 + src/rt/libuv/test/dns-server.c | 49 +- src/rt/libuv/test/echo-server.c | 198 +- src/rt/libuv/test/run-benchmarks.c | 43 +- src/rt/libuv/test/run-tests.c | 86 +- src/rt/libuv/test/runner-unix.c | 8 +- src/rt/libuv/test/runner-win.c | 26 +- src/rt/libuv/test/runner.c | 288 +- src/rt/libuv/test/runner.h | 38 +- src/rt/libuv/test/task.h | 26 +- src/rt/libuv/test/test-async.c | 15 +- src/rt/libuv/test/test-callback-stack.c | 44 +- src/rt/libuv/test/test-connection-fail.c | 29 +- src/rt/libuv/test/test-delayed-accept.c | 61 +- src/rt/libuv/test/test-fs-event.c | 217 + src/rt/libuv/test/test-fs.c | 1247 +++++ src/rt/libuv/test/test-get-currentexe.c | 14 +- src/rt/libuv/test/test-getaddrinfo.c | 40 +- src/rt/libuv/test/test-gethostbyname.c | 19 +- src/rt/libuv/test/test-getsockname.c | 342 ++ src/rt/libuv/test/test-hrtime.c | 19 +- src/rt/libuv/test/test-idle.c | 81 + src/rt/libuv/test/test-list.h | 162 +- src/rt/libuv/test/test-loop-handles.c | 88 +- src/rt/libuv/test/test-ping-pong.c | 165 +- src/rt/libuv/test/test-pipe-bind-error.c | 140 + src/rt/libuv/test/test-ref.c | 85 + src/rt/libuv/test/test-shutdown-eof.c | 31 +- src/rt/libuv/test/test-spawn.c | 383 ++ ...est-bind-error.c => test-tcp-bind-error.c} | 75 +- ...t-bind6-error.c => test-tcp-bind6-error.c} | 52 +- src/rt/libuv/test/test-tcp-close.c | 129 + src/rt/libuv/test/test-tcp-write-error.c | 154 + src/rt/libuv/test/test-tcp-writealot.c | 44 +- src/rt/libuv/test/test-threadpool.c | 57 + src/rt/libuv/test/test-timer-again.c | 28 +- src/rt/libuv/test/test-timer.c | 18 +- src/rt/libuv/test/test-tty.c | 56 + src/rt/libuv/test/test-udp-dgram-too-big.c | 86 + src/rt/libuv/test/test-udp-ipv6.c | 156 + src/rt/libuv/test/test-udp-send-and-recv.c | 208 + src/rt/libuv/uv.gyp | 339 ++ src/rt/libuv/vcbuild.bat | 93 + src/rt/rust_uv.cpp | 78 +- 213 files changed, 30554 insertions(+), 7209 deletions(-) create mode 100644 mk/libuv/mac/Makefile create mode 100755 mk/libuv/mac/gyp-mac-tool create mode 100644 mk/libuv/mac/src/rt/libuv/run-benchmarks.target.mk create mode 100644 mk/libuv/mac/src/rt/libuv/run-tests.target.mk create mode 100644 mk/libuv/mac/src/rt/libuv/uv.Makefile create mode 100644 mk/libuv/mac/src/rt/libuv/uv.target.mk create mode 100644 mk/libuv/unix/Makefile create mode 100644 mk/libuv/unix/src/rt/libuv/run-benchmarks.target.mk create mode 100644 mk/libuv/unix/src/rt/libuv/run-tests.target.mk create mode 100644 mk/libuv/unix/src/rt/libuv/uv.Makefile create mode 100644 mk/libuv/unix/src/rt/libuv/uv.target.mk create mode 100644 mk/libuv/win/Makefile create mode 100644 mk/libuv/win/src/rt/libuv/run-benchmarks.target.mk create mode 100644 mk/libuv/win/src/rt/libuv/run-tests.target.mk create mode 100644 mk/libuv/win/src/rt/libuv/uv.Makefile create mode 100644 mk/libuv/win/src/rt/libuv/uv.target.mk create mode 100755 src/etc/gyp-uv create mode 100644 src/rt/libuv/.mailmap delete mode 100644 src/rt/libuv/LIBUV_REVISION delete mode 100644 src/rt/libuv/README create mode 100644 src/rt/libuv/README.md create mode 100644 src/rt/libuv/common.gypi delete mode 100644 src/rt/libuv/doc/desired-api.md create mode 100755 src/rt/libuv/gyp_uv rename src/rt/libuv/include/{ => uv-private}/eio.h (78%) rename src/rt/libuv/include/{ => uv-private}/ev.h (96%) rename src/rt/libuv/include/{ => uv-private}/ngx-queue.h (100%) rename src/rt/libuv/include/{ => uv-private}/tree.h (100%) rename src/rt/libuv/{src/uv-freebsd.c => include/uv-private/uv-linux.h} (73%) rename src/rt/libuv/include/{ => uv-private}/uv-unix.h (59%) create mode 100644 src/rt/libuv/include/uv-private/uv-win.h delete mode 100644 src/rt/libuv/include/uv-win.h delete mode 100644 src/rt/libuv/msvs/c-ares.vcxproj delete mode 100644 src/rt/libuv/msvs/libuv-benchmark.vcxproj delete mode 100644 src/rt/libuv/msvs/libuv-test.vcxproj delete mode 100644 src/rt/libuv/msvs/libuv.sln delete mode 100644 src/rt/libuv/msvs/libuv.vcxproj create mode 100644 src/rt/libuv/src/ares/config_netbsd/ares_config.h delete mode 100644 src/rt/libuv/src/eio/Changes delete mode 100755 src/rt/libuv/src/eio/autogen.sh delete mode 100644 src/rt/libuv/src/eio/eio.pod create mode 100644 src/rt/libuv/src/unix/cares.c create mode 100644 src/rt/libuv/src/unix/core.c rename src/rt/libuv/src/{uv-cygwin.c => unix/cygwin.c} (83%) rename src/rt/libuv/src/{uv-darwin.c => unix/darwin.c} (82%) create mode 100644 src/rt/libuv/src/unix/eio/Changes rename src/rt/libuv/src/{ => unix}/eio/LICENSE (100%) rename src/rt/libuv/src/{ => unix}/eio/Makefile.am (81%) rename src/rt/libuv/src/{ => unix}/eio/aclocal.m4 (100%) create mode 100755 src/rt/libuv/src/unix/eio/autogen.sh rename src/rt/libuv/src/{ => unix}/eio/config.h.in (100%) rename src/rt/libuv/src/{ => unix}/eio/config_cygwin.h (97%) create mode 100644 src/rt/libuv/src/unix/eio/config_darwin.h rename src/rt/libuv/src/{ => unix}/eio/config_freebsd.h (97%) rename src/rt/libuv/src/{ => unix}/eio/config_linux.h (85%) rename src/rt/libuv/src/{eio/config_darwin.h => unix/eio/config_netbsd.h} (90%) rename src/rt/libuv/src/{ => unix}/eio/config_sunos.h (97%) rename src/rt/libuv/src/{ => unix}/eio/configure.ac (75%) rename src/rt/libuv/src/{ => unix}/eio/demo.c (100%) create mode 100644 src/rt/libuv/src/unix/eio/ecb.h rename src/rt/libuv/src/{ => unix}/eio/eio.3 (100%) rename src/rt/libuv/src/{ => unix}/eio/eio.c (63%) create mode 100644 src/rt/libuv/src/unix/eio/eio.pod rename src/rt/libuv/src/{ => unix}/eio/libeio.m4 (78%) rename src/rt/libuv/src/{ => unix}/eio/xthread.h (76%) create mode 100644 src/rt/libuv/src/unix/error.c rename src/rt/libuv/src/{ => unix}/ev/Changes (100%) rename src/rt/libuv/src/{ => unix}/ev/LICENSE (100%) rename src/rt/libuv/src/{ => unix}/ev/Makefile.am (100%) rename src/rt/libuv/src/{ => unix}/ev/Makefile.in (100%) rename src/rt/libuv/src/{ => unix}/ev/README (100%) rename src/rt/libuv/src/{ => unix}/ev/aclocal.m4 (100%) rename src/rt/libuv/src/{ => unix}/ev/autogen.sh (100%) rename src/rt/libuv/src/{ => unix}/ev/config.guess (100%) rename src/rt/libuv/src/{ => unix}/ev/config.h.in (100%) rename src/rt/libuv/src/{ => unix}/ev/config.sub (100%) rename src/rt/libuv/src/{ => unix}/ev/config_cygwin.h (100%) rename src/rt/libuv/src/{ => unix}/ev/config_darwin.h (100%) rename src/rt/libuv/src/{ => unix}/ev/config_freebsd.h (100%) rename src/rt/libuv/src/{ => unix}/ev/config_linux.h (80%) create mode 100644 src/rt/libuv/src/unix/ev/config_netbsd.h rename src/rt/libuv/src/{ => unix}/ev/config_sunos.h (100%) rename src/rt/libuv/src/{ => unix}/ev/configure (100%) rename src/rt/libuv/src/{ => unix}/ev/configure.ac (100%) rename src/rt/libuv/src/{ => unix}/ev/depcomp (100%) rename src/rt/libuv/src/{ => unix}/ev/ev++.h (100%) rename src/rt/libuv/src/{ => unix}/ev/ev.3 (100%) rename src/rt/libuv/src/{ => unix}/ev/ev.c (99%) rename src/rt/libuv/src/{ => unix}/ev/ev.pod (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_epoll.c (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_kqueue.c (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_poll.c (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_port.c (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_select.c (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_vars.h (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_win32.c (100%) rename src/rt/libuv/src/{ => unix}/ev/ev_wrap.h (100%) rename src/rt/libuv/src/{ => unix}/ev/event.c (100%) rename src/rt/libuv/src/{ => unix}/ev/event.h (100%) rename src/rt/libuv/src/{ => unix}/ev/install-sh (100%) rename src/rt/libuv/src/{ => unix}/ev/libev.m4 (100%) rename src/rt/libuv/src/{ => unix}/ev/ltmain.sh (100%) rename src/rt/libuv/src/{ => unix}/ev/missing (100%) rename src/rt/libuv/src/{ => unix}/ev/mkinstalldirs (100%) rename src/rt/libuv/src/{uv-linux.c => unix/freebsd.c} (69%) create mode 100644 src/rt/libuv/src/unix/fs.c create mode 100644 src/rt/libuv/src/unix/internal.h create mode 100644 src/rt/libuv/src/unix/linux.c create mode 100644 src/rt/libuv/src/unix/netbsd.c create mode 100644 src/rt/libuv/src/unix/pipe.c create mode 100644 src/rt/libuv/src/unix/process.c create mode 100644 src/rt/libuv/src/unix/stream.c rename src/rt/libuv/src/{uv-sunos.c => unix/sunos.c} (84%) create mode 100644 src/rt/libuv/src/unix/tcp.c create mode 100644 src/rt/libuv/src/unix/tty.c create mode 100644 src/rt/libuv/src/unix/udp.c rename src/rt/libuv/src/{ => unix}/uv-eio.c (59%) rename src/rt/libuv/src/{ => unix}/uv-eio.h (88%) delete mode 100644 src/rt/libuv/src/uv-unix.c delete mode 100644 src/rt/libuv/src/uv-win.c create mode 100644 src/rt/libuv/src/win/async.c create mode 100644 src/rt/libuv/src/win/cares.c create mode 100644 src/rt/libuv/src/win/core.c create mode 100644 src/rt/libuv/src/win/error.c create mode 100644 src/rt/libuv/src/win/fs-event.c create mode 100644 src/rt/libuv/src/win/fs.c create mode 100644 src/rt/libuv/src/win/getaddrinfo.c create mode 100644 src/rt/libuv/src/win/handle.c create mode 100644 src/rt/libuv/src/win/internal.h create mode 100644 src/rt/libuv/src/win/loop-watcher.c create mode 100644 src/rt/libuv/src/win/pipe.c create mode 100644 src/rt/libuv/src/win/process.c create mode 100644 src/rt/libuv/src/win/req.c create mode 100644 src/rt/libuv/src/win/stdio.c create mode 100644 src/rt/libuv/src/win/stream.c create mode 100644 src/rt/libuv/src/win/tcp.c create mode 100644 src/rt/libuv/src/win/threadpool.c create mode 100644 src/rt/libuv/src/win/threads.c create mode 100644 src/rt/libuv/src/win/timer.c create mode 100644 src/rt/libuv/src/win/tty.c create mode 100644 src/rt/libuv/src/win/udp.c create mode 100644 src/rt/libuv/src/win/util.c create mode 100644 src/rt/libuv/src/win/winapi.c create mode 100644 src/rt/libuv/src/win/winapi.h create mode 100644 src/rt/libuv/src/win/winsock.c create mode 100644 src/rt/libuv/src/win/winsock.h create mode 100644 src/rt/libuv/test/benchmark-pound.c create mode 100644 src/rt/libuv/test/benchmark-spawn.c create mode 100644 src/rt/libuv/test/benchmark-udp-packet-storm.c create mode 100644 src/rt/libuv/test/test-fs-event.c create mode 100644 src/rt/libuv/test/test-fs.c create mode 100644 src/rt/libuv/test/test-getsockname.c create mode 100644 src/rt/libuv/test/test-idle.c create mode 100644 src/rt/libuv/test/test-pipe-bind-error.c create mode 100644 src/rt/libuv/test/test-ref.c create mode 100644 src/rt/libuv/test/test-spawn.c rename src/rt/libuv/test/{test-bind-error.c => test-tcp-bind-error.c} (71%) rename src/rt/libuv/test/{test-bind6-error.c => test-tcp-bind6-error.c} (76%) create mode 100644 src/rt/libuv/test/test-tcp-close.c create mode 100644 src/rt/libuv/test/test-tcp-write-error.c create mode 100644 src/rt/libuv/test/test-threadpool.c create mode 100644 src/rt/libuv/test/test-tty.c create mode 100644 src/rt/libuv/test/test-udp-dgram-too-big.c create mode 100644 src/rt/libuv/test/test-udp-ipv6.c create mode 100644 src/rt/libuv/test/test-udp-send-and-recv.c create mode 100644 src/rt/libuv/uv.gyp create mode 100644 src/rt/libuv/vcbuild.bat diff --git a/.gitignore b/.gitignore index 5c9304ddc5e..2517b30ae92 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,7 @@ rustc TAGS version.ml version.texi -Makefile +./Makefile config.mk /rt/ /rustllvm/ diff --git a/configure b/configure index 881874abd70..10c4d7df32c 100755 --- a/configure +++ b/configure @@ -406,8 +406,4 @@ done copy ${CFG_SRC_DIR}Makefile.in ./Makefile -copy ${CFG_SRC_DIR}src/rt/libuv/Makefile rt/libuv/Makefile -copy ${CFG_SRC_DIR}src/rt/libuv/config-unix.mk rt/libuv/config-unix.mk -copy ${CFG_SRC_DIR}src/rt/libuv/config-mingw.mk rt/libuv/config-mingw.mk - step_msg "complete" diff --git a/mk/clean.mk b/mk/clean.mk index 9449c203ec8..682b536ed9b 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -54,3 +54,4 @@ clean: aux cp fn ky log pdf html pg toc tp vr cps, \ $(wildcard doc/*.$(ext))) $(Q)rm -Rf doc/version.texi + $(Q)rm -rf rt/libuv diff --git a/mk/libuv/mac/Makefile b/mk/libuv/mac/Makefile new file mode 100644 index 00000000000..b1e21e5f93e --- /dev/null +++ b/mk/libuv/mac/Makefile @@ -0,0 +1,359 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := ../../.. + +# The name of the builddir. +builddir_name ?= out + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Default + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + +# C++ apps need to be linked with g++. Not sure what's appropriate. +# +# Note, the flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override FLOCK via an envrionment variable as follows: +# +# export FLOCK= +# +# This will allow make to invoke N linker processes as specified in -jN. +FLOCK ?= ./gyp-mac-tool flock $(builddir)/linker.lock + + + +LINK ?= $(FLOCK) $(CXX) +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crs + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crs + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CCFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && libtool -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): Find out and document the difference between shared_library and +# loadable_module on mac. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass +# -bundle -single_module here (for osmesa.so). +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + @for p in $(POSTBUILDS); do eval $$p; done + ) +) +endef + +# Declare "all" target first so it is the default, even though we don't have the +# deps yet. +.PHONY: all +all: + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-benchmarks.target.mk)))),) + include src/rt/libuv/run-benchmarks.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-tests.target.mk)))),) + include src/rt/libuv/run-tests.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/uv.target.mk)))),) + include src/rt/libuv/uv.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = ./src/rt/libuv/build/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." "--depth=." "--generator-output=mk/libuv/mac" "-Dlibrary=static_library" "-Dtarget_arch=ia32" "-DOS=mac" src/rt/libuv/uv.gyp +#Makefile: $(srcdir)/src/rt/libuv/uv.gyp +# $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + # Rather than include each individual .d file, concatenate them into a + # single file which make is able to load faster. We split this into + # commands that take 1000 files at a time to avoid overflowing the + # command line. + $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps) + + ifneq ($(word 1001,$(d_files)),) + $(error Found unprocessed dependency files (gyp didn't generate enough rules!)) + endif + + # make looks for ways to re-generate included makefiles, but in our case, we + # don't have a direct way. Explicitly telling make that it has nothing to do + # for them makes it go faster. + $(depsdir)/all.deps: ; + + include $(depsdir)/all.deps +endif diff --git a/mk/libuv/mac/gyp-mac-tool b/mk/libuv/mac/gyp-mac-tool new file mode 100755 index 00000000000..7eb894b23ef --- /dev/null +++ b/mk/libuv/mac/gyp-mac-tool @@ -0,0 +1,188 @@ +#!/usr/bin/python +# Generated by gyp. Do not edit. +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import os +import fcntl +import plistlib +import shutil +import string +import subprocess +import sys + +def main(args): + executor = MacTool() + executor.Dispatch(args) + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecCopyInfoPlist(self, source, dest): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Go through all the environment variables and replace them as variables in + # the file. + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + lines = string.replace(lines, evar, os.environ[key]) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If either is missing, four + # '?' characters are used instead. + package_type = plist['CFBundlePackageType'] + if len(package_type) != 4: + package_type = '?' * 4 + signature_code = plist['CFBundleSignature'] + if len(signature_code) != 4: + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + def ExecCopyBundleResource(self, source, dest): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest) + # TODO: Given that files with arbitrary extensions can be copied to the + # bundle, we will want to get rid of this whitelist eventually. + elif extension in [ + '.icns', '.manifest', '.pak', '.pdf', '.png', '.sb', '.sh', + '.ttf', '.sdef']: + shutil.copyfile(source, dest) + else: + raise NotImplementedError( + "Don't know how to copy bundle resources of type %s while copying " + "%s to %s)" % (extension, source, dest)) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + args = ['/Developer/usr/bin/ibtool', '--errors', '--warnings', + '--notices', '--output-format', 'human-readable-text', '--compile', + dest, source] + subprocess.call(args) + + def _CopyStringsFile(self, source, dest): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + fp = open(dest, 'w') + args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code', + 'UTF-16', source] + subprocess.call(args, stdout=fp) + fp.close() + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16BE" + elif header.startswith("\xFF\xFE"): + return "UTF-16LE" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/mk/libuv/mac/src/rt/libuv/run-benchmarks.target.mk b/mk/libuv/mac/src/rt/libuv/run-benchmarks.target.mk new file mode 100644 index 00000000000..df9853a5040 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/run-benchmarks.target.mk @@ -0,0 +1,91 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-benchmarks +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := -fasm-blocks \ + -mpascal-strings \ + -gdwarf-2 \ + -arch i386 + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Default := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ping-pongs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pound.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pump.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-sizes.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-udp-packet-storm.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/dns-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-benchmarks.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(builddir)/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -arch i386 \ + -L$(builddir) + +LIBS := -framework Carbon \ + -framework CoreServices + +$(builddir)/run-benchmarks: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-benchmarks: LIBS := $(LIBS) +$(builddir)/run-benchmarks: LD_INPUTS := $(OBJS) $(builddir)/libuv.a +$(builddir)/run-benchmarks: TOOLSET := $(TOOLSET) +$(builddir)/run-benchmarks: $(OBJS) $(builddir)/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-benchmarks +# Add target alias +.PHONY: run-benchmarks +run-benchmarks: $(builddir)/run-benchmarks + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-benchmarks + diff --git a/mk/libuv/mac/src/rt/libuv/run-tests.target.mk b/mk/libuv/mac/src/rt/libuv/run-tests.target.mk new file mode 100644 index 00000000000..bb40dc55de6 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/run-tests.target.mk @@ -0,0 +1,114 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-tests +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := -fasm-blocks \ + -mpascal-strings \ + -gdwarf-2 \ + -arch i386 + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Default := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-tests.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-callback-stack.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-connection-fail.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-delayed-accept.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fail-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-get-currentexe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getsockname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-hrtime.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-idle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-loop-handles.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pass-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ping-pong.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pipe-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ref.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-shutdown-eof.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind6-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-close.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-write-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-writealot.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer-again.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-dgram-too-big.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-ipv6.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-send-and-recv.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(builddir)/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -arch i386 \ + -L$(builddir) + +LIBS := -framework Carbon \ + -framework CoreServices + +$(builddir)/run-tests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-tests: LIBS := $(LIBS) +$(builddir)/run-tests: LD_INPUTS := $(OBJS) $(builddir)/libuv.a +$(builddir)/run-tests: TOOLSET := $(TOOLSET) +$(builddir)/run-tests: $(OBJS) $(builddir)/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-tests +# Add target alias +.PHONY: run-tests +run-tests: $(builddir)/run-tests + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-tests + diff --git a/mk/libuv/mac/src/rt/libuv/uv.Makefile b/mk/libuv/mac/src/rt/libuv/uv.Makefile new file mode 100644 index 00000000000..fd944e922e5 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/uv.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= mk/libuv/mac/./src/rt/libuv/out +.PHONY: all +all: + $(MAKE) -C ../../.. uv run-tests run-benchmarks diff --git a/mk/libuv/mac/src/rt/libuv/uv.target.mk b/mk/libuv/mac/src/rt/libuv/uv.target.mk new file mode 100644 index 00000000000..dce473cf2f7 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/uv.target.mk @@ -0,0 +1,141 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := uv +DEFS_Default := '-DHAVE_CONFIG_H' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_GNU_SOURCE' \ + '-DEIO_STACKSIZE=262144' \ + '-DEV_CONFIG_H="config_darwin.h"' \ + '-DEIO_CONFIG_H="config_darwin.h"' + +# Flags passed to all source files. +CFLAGS_Default := -fasm-blocks \ + -mpascal-strings \ + -gdwarf-2 \ + -arch i386 + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Default := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include \ + -I$(srcdir)/src/rt/libuv/include/uv-private \ + -I$(srcdir)/src/rt/libuv/src \ + -I$(srcdir)/src/rt/libuv/src/unix/ev \ + -I$(srcdir)/src/rt/libuv/src/ares/config_darwin + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/src/uv-common.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__close_sockets.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__get_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__read_line.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__timeval.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_cancel.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_data.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_destroy.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_name.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_fds.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyaddr.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getnameinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getopt.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getsock.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_library_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_llist.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_mkquery.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_nowarn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_options.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_a_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_aaaa_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_mx_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ns_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ptr_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_srv_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_txt_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_query.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_search.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_send.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strcasecmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strdup.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strerror.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_timeout.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_version.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_writev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/bitncmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_net_pton.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_ntop.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/core.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/uv-eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/udp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tcp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/pipe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/stream.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/cares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/eio/eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/ev/ev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/darwin.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -arch i386 \ + -L$(builddir) + +LIBS := -lm + +$(builddir)/libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/libuv.a: LIBS := $(LIBS) +$(builddir)/libuv.a: TOOLSET := $(TOOLSET) +$(builddir)/libuv.a: $(OBJS) FORCE_DO_CMD + $(call do_cmd,alink) + +all_deps += $(builddir)/libuv.a +# Add target alias +.PHONY: uv +uv: $(builddir)/libuv.a + +# Add target alias to "all" target. +.PHONY: all +all: uv + diff --git a/mk/libuv/unix/Makefile b/mk/libuv/unix/Makefile new file mode 100644 index 00000000000..89d244fc025 --- /dev/null +++ b/mk/libuv/unix/Makefile @@ -0,0 +1,337 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := ../../.. + +# The name of the builddir. +builddir_name ?= out + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Default + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + +# C++ apps need to be linked with g++. Not sure what's appropriate. +# +# Note, the flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override FLOCK via an envrionment variable as follows: +# +# export FLOCK= +# +# This will allow make to invoke N linker processes as specified in -jN. +FLOCK ?= flock $(builddir)/linker.lock + + + +LINK ?= $(FLOCK) $(CXX) +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crsT + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crsT + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = AR($(TOOLSET)) $@ +cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter %.o,$^) + +# Due to circular dependencies between libraries :(, we wrap the +# special "figure out circular dependencies" flags around the entire +# input list during linking. +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS) + +# We support two kinds of shared objects (.so): +# 1) shared_library, which is just bundling together many dependent libraries +# into a link line. +# 2) loadable_module, which is generating a module intended for dlopen(). +# +# They differ only slightly: +# In the former case, we want to package all dependent code into the .so. +# In the latter case, we want to package just the API exposed by the +# outermost module. +# This means shared_library uses --whole-archive, while loadable_module doesn't. +# (Note that --whole-archive is incompatible with the --start-group used in +# normal linking.) + +# Other shared-object link notes: +# - Set SONAME to the library filename so our binaries don't reference +# the local, absolute paths used on the link command-line. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 1,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + @for p in $(POSTBUILDS); do eval $$p; done + ) +) +endef + +# Declare "all" target first so it is the default, even though we don't have the +# deps yet. +.PHONY: all +all: + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-benchmarks.target.mk)))),) + include src/rt/libuv/run-benchmarks.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-tests.target.mk)))),) + include src/rt/libuv/run-tests.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/uv.target.mk)))),) + include src/rt/libuv/uv.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = ./src/rt/libuv/build/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." "--depth=." "--generator-output=mk/libuv/unix" "-Dlibrary=static_library" "-Dtarget_arch=ia32" "-DOS=linux" src/rt/libuv/uv.gyp +#Makefile: $(srcdir)/src/rt/libuv/uv.gyp +# $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + # Rather than include each individual .d file, concatenate them into a + # single file which make is able to load faster. We split this into + # commands that take 1000 files at a time to avoid overflowing the + # command line. + $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps) + + ifneq ($(word 1001,$(d_files)),) + $(error Found unprocessed dependency files (gyp didn't generate enough rules!)) + endif + + # make looks for ways to re-generate included makefiles, but in our case, we + # don't have a direct way. Explicitly telling make that it has nothing to do + # for them makes it go faster. + $(depsdir)/all.deps: ; + + include $(depsdir)/all.deps +endif diff --git a/mk/libuv/unix/src/rt/libuv/run-benchmarks.target.mk b/mk/libuv/unix/src/rt/libuv/run-benchmarks.target.mk new file mode 100644 index 00000000000..ad3900cea63 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/run-benchmarks.target.mk @@ -0,0 +1,78 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-benchmarks +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ping-pongs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pound.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pump.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-sizes.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-udp-packet-storm.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/dns-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-benchmarks.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -pthread + +LIBS := -lrt + +$(builddir)/run-benchmarks: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-benchmarks: LIBS := $(LIBS) +$(builddir)/run-benchmarks: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-benchmarks: TOOLSET := $(TOOLSET) +$(builddir)/run-benchmarks: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-benchmarks +# Add target alias +.PHONY: run-benchmarks +run-benchmarks: $(builddir)/run-benchmarks + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-benchmarks + diff --git a/mk/libuv/unix/src/rt/libuv/run-tests.target.mk b/mk/libuv/unix/src/rt/libuv/run-tests.target.mk new file mode 100644 index 00000000000..605892fea05 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/run-tests.target.mk @@ -0,0 +1,101 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-tests +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-tests.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-callback-stack.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-connection-fail.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-delayed-accept.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fail-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-get-currentexe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getsockname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-hrtime.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-idle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-loop-handles.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pass-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ping-pong.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pipe-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ref.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-shutdown-eof.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind6-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-close.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-write-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-writealot.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer-again.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-dgram-too-big.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-ipv6.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-send-and-recv.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -pthread + +LIBS := -lrt + +$(builddir)/run-tests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-tests: LIBS := $(LIBS) +$(builddir)/run-tests: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-tests: TOOLSET := $(TOOLSET) +$(builddir)/run-tests: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-tests +# Add target alias +.PHONY: run-tests +run-tests: $(builddir)/run-tests + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-tests + diff --git a/mk/libuv/unix/src/rt/libuv/uv.Makefile b/mk/libuv/unix/src/rt/libuv/uv.Makefile new file mode 100644 index 00000000000..579a5915782 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/uv.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= mk/libuv/unix/./src/rt/libuv/out +.PHONY: all +all: + $(MAKE) -C ../../.. uv run-tests run-benchmarks diff --git a/mk/libuv/unix/src/rt/libuv/uv.target.mk b/mk/libuv/unix/src/rt/libuv/uv.target.mk new file mode 100644 index 00000000000..dc5264341b7 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/uv.target.mk @@ -0,0 +1,134 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := uv +DEFS_Default := '-DHAVE_CONFIG_H' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_GNU_SOURCE' \ + '-DEIO_STACKSIZE=262144' \ + '-DEV_CONFIG_H="config_linux.h"' \ + '-DEIO_CONFIG_H="config_linux.h"' + +# Flags passed to all source files. +CFLAGS_Default := -g \ + --std=gnu89 \ + -pedantic \ + -Wall \ + -Wextra \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include \ + -I$(srcdir)/src/rt/libuv/include/uv-private \ + -I$(srcdir)/src/rt/libuv/src \ + -I$(srcdir)/src/rt/libuv/src/unix/ev \ + -I$(srcdir)/src/rt/libuv/src/ares/config_linux + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/src/uv-common.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__close_sockets.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__get_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__read_line.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__timeval.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_cancel.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_data.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_destroy.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_name.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_fds.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyaddr.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getnameinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getopt.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getsock.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_library_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_llist.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_mkquery.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_nowarn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_options.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_a_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_aaaa_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_mx_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ns_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ptr_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_srv_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_txt_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_query.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_search.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_send.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strcasecmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strdup.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strerror.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_timeout.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_version.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_writev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/bitncmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_net_pton.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_ntop.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/core.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/uv-eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/udp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tcp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/pipe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/stream.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/cares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/eio/eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/ev/ev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/linux.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := -lm + +$(obj).target/src/rt/libuv/libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(obj).target/src/rt/libuv/libuv.a: LIBS := $(LIBS) +$(obj).target/src/rt/libuv/libuv.a: TOOLSET := $(TOOLSET) +$(obj).target/src/rt/libuv/libuv.a: $(OBJS) FORCE_DO_CMD + $(call do_cmd,alink) + +all_deps += $(obj).target/src/rt/libuv/libuv.a +# Add target alias +.PHONY: uv +uv: $(obj).target/src/rt/libuv/libuv.a + +# Add target alias to "all" target. +.PHONY: all +all: uv + diff --git a/mk/libuv/win/Makefile b/mk/libuv/win/Makefile new file mode 100644 index 00000000000..5808ed4c3fc --- /dev/null +++ b/mk/libuv/win/Makefile @@ -0,0 +1,337 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := ../../.. + +# The name of the builddir. +builddir_name ?= out + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Default + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + +# C++ apps need to be linked with g++. Not sure what's appropriate. +# +# Note, the flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override FLOCK via an envrionment variable as follows: +# +# export FLOCK= +# +# This will allow make to invoke N linker processes as specified in -jN. +FLOCK ?= flock $(builddir)/linker.lock + + + +LINK ?= $(FLOCK) $(CXX) +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crsT + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crsT + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = AR($(TOOLSET)) $@ +cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter %.o,$^) + +# Due to circular dependencies between libraries :(, we wrap the +# special "figure out circular dependencies" flags around the entire +# input list during linking. +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS) + +# We support two kinds of shared objects (.so): +# 1) shared_library, which is just bundling together many dependent libraries +# into a link line. +# 2) loadable_module, which is generating a module intended for dlopen(). +# +# They differ only slightly: +# In the former case, we want to package all dependent code into the .so. +# In the latter case, we want to package just the API exposed by the +# outermost module. +# This means shared_library uses --whole-archive, while loadable_module doesn't. +# (Note that --whole-archive is incompatible with the --start-group used in +# normal linking.) + +# Other shared-object link notes: +# - Set SONAME to the library filename so our binaries don't reference +# the local, absolute paths used on the link command-line. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 1,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + @for p in $(POSTBUILDS); do eval $$p; done + ) +) +endef + +# Declare "all" target first so it is the default, even though we don't have the +# deps yet. +.PHONY: all +all: + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-benchmarks.target.mk)))),) + include src/rt/libuv/run-benchmarks.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-tests.target.mk)))),) + include src/rt/libuv/run-tests.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/uv.target.mk)))),) + include src/rt/libuv/uv.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = ./src/rt/libuv/build/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." "--depth=." "--generator-output=mk/libuv/win" "-Dlibrary=static_library" "-Dtarget_arch=ia32" "-DOS=win" src/rt/libuv/uv.gyp +#Makefile: $(srcdir)/src/rt/libuv/uv.gyp +# $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + # Rather than include each individual .d file, concatenate them into a + # single file which make is able to load faster. We split this into + # commands that take 1000 files at a time to avoid overflowing the + # command line. + $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps) + + ifneq ($(word 1001,$(d_files)),) + $(error Found unprocessed dependency files (gyp didn't generate enough rules!)) + endif + + # make looks for ways to re-generate included makefiles, but in our case, we + # don't have a direct way. Explicitly telling make that it has nothing to do + # for them makes it go faster. + $(depsdir)/all.deps: ; + + include $(depsdir)/all.deps +endif diff --git a/mk/libuv/win/src/rt/libuv/run-benchmarks.target.mk b/mk/libuv/win/src/rt/libuv/run-benchmarks.target.mk new file mode 100644 index 00000000000..0a3cbf31b5b --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/run-benchmarks.target.mk @@ -0,0 +1,79 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-benchmarks +DEFS_Default := + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ping-pongs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pound.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pump.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-sizes.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-udp-packet-storm.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/dns-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-benchmarks.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-win.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := ws2_32.lib \ + -lws2_32.lib + +$(builddir)/run-benchmarks: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-benchmarks: LIBS := $(LIBS) +$(builddir)/run-benchmarks: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-benchmarks: TOOLSET := $(TOOLSET) +$(builddir)/run-benchmarks: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-benchmarks +# Add target alias +.PHONY: run-benchmarks +run-benchmarks: $(builddir)/run-benchmarks + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-benchmarks + diff --git a/mk/libuv/win/src/rt/libuv/run-tests.target.mk b/mk/libuv/win/src/rt/libuv/run-tests.target.mk new file mode 100644 index 00000000000..438b41ea520 --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/run-tests.target.mk @@ -0,0 +1,102 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-tests +DEFS_Default := + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-tests.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-callback-stack.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-connection-fail.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-delayed-accept.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fail-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-get-currentexe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getsockname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-hrtime.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-idle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-loop-handles.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pass-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ping-pong.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pipe-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ref.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-shutdown-eof.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind6-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-close.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-write-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-writealot.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer-again.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-dgram-too-big.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-ipv6.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-send-and-recv.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-win.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := ws2_32.lib \ + -lws2_32.lib + +$(builddir)/run-tests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-tests: LIBS := $(LIBS) +$(builddir)/run-tests: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-tests: TOOLSET := $(TOOLSET) +$(builddir)/run-tests: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-tests +# Add target alias +.PHONY: run-tests +run-tests: $(builddir)/run-tests + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-tests + diff --git a/mk/libuv/win/src/rt/libuv/uv.Makefile b/mk/libuv/win/src/rt/libuv/uv.Makefile new file mode 100644 index 00000000000..f7bfa935ff3 --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/uv.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= mk/libuv/win/./src/rt/libuv/out +.PHONY: all +all: + $(MAKE) -C ../../.. uv run-tests run-benchmarks diff --git a/mk/libuv/win/src/rt/libuv/uv.target.mk b/mk/libuv/win/src/rt/libuv/uv.target.mk new file mode 100644 index 00000000000..e8f7a135ac5 --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/uv.target.mk @@ -0,0 +1,135 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := uv +DEFS_Default := '-DHAVE_CONFIG_H' \ + '-D_WIN32_WINNT=0x0502' \ + '-DEIO_STACKSIZE=262144' \ + '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include \ + -I$(srcdir)/src/rt/libuv/include/uv-private \ + -I$(srcdir)/src/rt/libuv/src \ + -I$(srcdir)/src/rt/libuv/src/ares/config_win32 + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/src/uv-common.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__close_sockets.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__get_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__read_line.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__timeval.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_cancel.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_data.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_destroy.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_name.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_fds.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyaddr.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getnameinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getopt.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getsock.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_library_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_llist.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_mkquery.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_nowarn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_options.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_a_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_aaaa_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_mx_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ns_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ptr_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_srv_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_txt_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_query.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_search.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_send.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strcasecmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strdup.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strerror.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_timeout.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_version.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_writev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/bitncmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_net_pton.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_ntop.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/windows_port.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/cares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/core.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/handle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/loop-watcher.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/pipe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/req.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/stdio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/stream.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/tcp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/threads.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/udp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/util.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/winapi.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/winsock.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := + +$(obj).target/src/rt/libuv/libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(obj).target/src/rt/libuv/libuv.a: LIBS := $(LIBS) +$(obj).target/src/rt/libuv/libuv.a: TOOLSET := $(TOOLSET) +$(obj).target/src/rt/libuv/libuv.a: $(OBJS) FORCE_DO_CMD + $(call do_cmd,alink) + +all_deps += $(obj).target/src/rt/libuv/libuv.a +# Add target alias +.PHONY: uv +uv: $(obj).target/src/rt/libuv/libuv.a + +# Add target alias to "all" target. +.PHONY: all +all: uv + diff --git a/mk/platform.mk b/mk/platform.mk index a58e75ace81..a74c4017fec 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -50,7 +50,7 @@ ifneq ($(findstring darwin,$(CFG_OSTYPE)),) CFG_LIB_NAME=lib$(1).dylib CFG_UNIXY := 1 CFG_LDENV := DYLD_LIBRARY_PATH - CFG_GCCISH_LINK_FLAGS += -dynamiclib -lpthread + CFG_GCCISH_LINK_FLAGS += -dynamiclib -lpthread -framework CoreServices CFG_GCCISH_DEF_FLAG := -Wl,-exported_symbols_list, # Darwin has a very blurry notion of "64 bit", and claims it's running # "on an i386" when the whole userspace is 64-bit and the compiler diff --git a/mk/rt.mk b/mk/rt.mk index f2ed226b266..41fa5eb9880 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -68,11 +68,22 @@ RUNTIME_HDR := rt/globals.h \ rt/test/rust_test_util.h \ rt/arch/i386/context.h \ +ifeq ($(CFG_WINDOWSY), 1) + LIBUV_OSTYPE := win + LIBUV_LIB := rt/libuv/Default/obj.target/src/rt/libuv/libuv.a +else ifeq ($(CFG_OSTYPE), apple-darwin) + LIBUV_OSTYPE := mac + LIBUV_LIB := rt/libuv/Default/libuv.a +else + LIBUV_OSTYPE := unix + LIBUV_LIB := rt/libuv/Default/obj.target/src/rt/libuv/libuv.a +endif + RUNTIME_DEF := rt/rustrt$(CFG_DEF_SUFFIX) RUNTIME_INCS := -I $(S)src/rt/isaac -I $(S)src/rt/uthash \ -I $(S)src/rt/arch/i386 -I $(S)src/rt/libuv/include RUNTIME_OBJS := $(RUNTIME_CS:.cpp=.o) $(RUNTIME_LL:.ll=.o) $(RUNTIME_S:.S=.o) -RUNTIME_LIBS := rt/libuv/uv.a +RUNTIME_LIBS := $(LIBUV_LIB) rt/%.o: rt/%.cpp $(MKFILES) @$(call E, compile: $@) @@ -105,12 +116,18 @@ rt/$(CFG_RUNTIME): $(RUNTIME_OBJS) $(MKFILES) $(RUNTIME_HDR) $(RUNTIME_DEF) $(RU # FIXME: For some reason libuv's makefiles can't figure out the correct definition # of CC on the mingw I'm using, so we are explicitly using gcc. Also, we # have to list environment variables first on windows... mysterious -rt/libuv/uv.a: $(wildcard \ +$(LIBUV_LIB): $(wildcard \ $(S)src/rt/libuv/* \ $(S)src/rt/libuv/*/* \ $(S)src/rt/libuv/*/*/* \ $(S)src/rt/libuv/*/*/*/*) - $(Q)CFLAGS=\"-m32\" LDFLAGS=\"-m32\" CC=$(CC) $(MAKE) -C rt/libuv + $(Q)$(MAKE) -C $(S)mk/libuv/$(LIBUV_OSTYPE) \ + CFLAGS="-m32" LDFLAGS="-m32" \ + CC="$(CFG_GCCISH_CROSS)$(CC)" \ + CXX="$(CFG_GCCISH_CROSS)$(CXX)" \ + AR="$(CFG_GCCISH_CROSS)$(AR)" \ + builddir_name="$(CFG_BUILD_DIR)/rt/libuv" \ + V=$(VERBOSE) FLOCK= uv # These could go in rt.mk or rustllvm.mk, they're needed for both. diff --git a/src/etc/gyp-uv b/src/etc/gyp-uv new file mode 100755 index 00000000000..1e24f5eaa37 --- /dev/null +++ b/src/etc/gyp-uv @@ -0,0 +1,36 @@ +#!/bin/sh + +set -e + +cd `dirname $0` +cd ../.. + +args="--depth . -Dlibrary=static_library -Dtarget_arch=ia32" + +./src/rt/libuv/build/gyp/gyp src/rt/libuv/uv.gyp $args \ + -f make-mac \ + --generator-output mk/libuv/mac \ + -DOS=mac + +./src/rt/libuv/build/gyp/gyp src/rt/libuv/uv.gyp $args \ + -f make-linux \ + --generator-output mk/libuv/unix \ + -DOS=linux + +./src/rt/libuv/build/gyp/gyp src/rt/libuv/uv.gyp $args \ + -f make-linux \ + --generator-output mk/libuv/win \ + -DOS=win + +# Comment out the gyp auto regeneration +for os in mac unix win; do + sed -i ".save" \ + -e 's/^\(Makefile: $(srcdir)\/src\/rt\/libuv\/uv\.gyp\)/#\1/' \ + mk/libuv/$os/Makefile + + sed -i ".save" \ + -e 's/^\( $(call do_cmd,regen_makefile)\)/#\1/' \ + mk/libuv/$os/Makefile + + rm mk/libuv/$os/Makefile.save +done diff --git a/src/rt/libuv/.gitignore b/src/rt/libuv/.gitignore index 9d142354bf3..09164c65eb0 100644 --- a/src/rt/libuv/.gitignore +++ b/src/rt/libuv/.gitignore @@ -7,37 +7,23 @@ *.orig *.sdf *.suo +/out/ +/build/gyp + +/test/run-tests +/test/run-tests.exe +/test/run-tests.dSYM +/test/run-benchmarks +/test/run-benchmarks.exe +/test/run-benchmarks.dSYM + +*.sln +*.vcproj +*.vcxproj *.vcxproj.filters *.vcxproj.user -ev/.deps/ -ev/.libs/ -ev/Makefile -ev/config.h -ev/config.log -ev/config.status -ev/libtool -ev/stamp-h1 -ev/autom4te.cache -/msvs/ipch/ -/build/ -test/run-tests -test/run-benchmarks -test/run-tests.exe -test/run-benchmarks.exe -test/run-benchmarks.dSYM/ -test/run-tests.dSYM/ - - -c-ares/.deps/ -c-ares/.libs/ -c-ares/Makefile -c-ares/acountry -c-ares/adig -c-ares/ahost -c-ares/ares_config.h -c-ares/config.log -c-ares/config.status -c-ares/libcares.pc -c-ares/libtool -c-ares/stamp-h1 -c-ares/stamp-h2 +_UpgradeReport_Files/ +UpgradeLog*.XML +Debug +Release +ipch diff --git a/src/rt/libuv/.mailmap b/src/rt/libuv/.mailmap new file mode 100644 index 00000000000..520f71be68f --- /dev/null +++ b/src/rt/libuv/.mailmap @@ -0,0 +1,8 @@ +# update AUTHORS with: +# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS + + + + +San-Tai Hsu +Isaac Z. Schlueter diff --git a/src/rt/libuv/AUTHORS b/src/rt/libuv/AUTHORS index 4b4cab70f13..eefcd7dc1ee 100644 --- a/src/rt/libuv/AUTHORS +++ b/src/rt/libuv/AUTHORS @@ -3,9 +3,24 @@ Ryan Dahl Bert Belder Josh Roesslein Alan Gutierrez +Joshua Peek Igor Zinkovsky -Vanilla Hsu +San-Tai Hsu Ben Noordhuis Henry Rawas Robert Mustacchi Matt Stevens +Paul Querna +Shigeki Ohtsu +Tom Hughes +Peter Bright +Jeroen Janssen +Andrea Lattuada +Augusto Henrique Hentz +Clifford Heath +Jorge Chamorro Bieling +Luis Lavena +Matthew Sporleder +Erick Tryzelaar +Isaac Z. Schlueter +Pieter Noordhuis diff --git a/src/rt/libuv/LIBUV_REVISION b/src/rt/libuv/LIBUV_REVISION deleted file mode 100644 index f079f657765..00000000000 --- a/src/rt/libuv/LIBUV_REVISION +++ /dev/null @@ -1,5 +0,0 @@ -This subtree is pulled from: -ee599ec1141cc48f895de1f9d148033babdf9c2a - -When pulling in a new version of libuv, please update this file to ensure that -everyone correctly rebuilds libuv. diff --git a/src/rt/libuv/Makefile b/src/rt/libuv/Makefile index b8d3c685952..59be3de08f3 100644 --- a/src/rt/libuv/Makefile +++ b/src/rt/libuv/Makefile @@ -18,30 +18,13 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -include ../../config.mk -CFLAGS=-m32 -LDFLAGS=-m32 -CC=gcc - -S:=$(shell cd ../../; cd $(CFG_SRC_DIR)src/rt/libuv; pwd) - -ifdef VERBOSE - Q := - EE = -else - Q := @ - EE = echo $(1) -endif - -VPATH:=$(S) - uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') ifdef MSVC uname_S := MINGW endif -CPPFLAGS += -I$(S)/include +CPPFLAGS += -Iinclude -Iinclude/uv-private CARES_OBJS = CARES_OBJS += src/ares/ares__close_sockets.o @@ -94,8 +77,43 @@ else include config-unix.mk endif -all: uv.a +TESTS=test/echo-server.c test/test-*.c +BENCHMARKS=test/echo-server.c test/dns-server.c test/benchmark-*.c + +all: uv.a test/run-tests$(E) test/run-benchmarks$(E) $(CARES_OBJS): %.o: %.c - @$(call EE, compile: $@) - $(Q)$(CC) -o $*.o -c $(CFLAGS) $(CPPFLAGS) $< -DHAVE_CONFIG_H + $(CC) -o $*.o -c $(CFLAGS) $(CPPFLAGS) $< -DHAVE_CONFIG_H + +test/run-tests$(E): test/*.h test/run-tests.c $(RUNNER_SRC) test/runner-unix.c $(TESTS) uv.a + $(CC) $(CPPFLAGS) $(RUNNER_CFLAGS) -o test/run-tests test/run-tests.c \ + test/runner.c $(RUNNER_SRC) $(TESTS) uv.a $(RUNNER_LIBS) $(RUNNER_LINKFLAGS) + +test/run-benchmarks$(E): test/*.h test/run-benchmarks.c test/runner.c $(RUNNER_SRC) $(BENCHMARKS) uv.a + $(CC) $(CPPFLAGS) $(RUNNER_CFLAGS) -o test/run-benchmarks test/run-benchmarks.c \ + test/runner.c $(RUNNER_SRC) $(BENCHMARKS) uv.a $(RUNNER_LIBS) $(RUNNER_LINKFLAGS) + +test/echo.o: test/echo.c test/echo.h + $(CC) $(CPPFLAGS) $(CFLAGS) -c test/echo.c -o test/echo.o + + +.PHONY: clean clean-platform distclean distclean-platform test bench + + +test: test/run-tests$(E) + test/run-tests + +#test-%: test/run-tests$(E) +# test/run-tests $(@:test-%=%) + +bench: test/run-benchmarks$(E) + test/run-benchmarks + +#bench-%: test/run-benchmarks$(E) +# test/run-benchmarks $(@:bench-%=%) + +clean: clean-platform + $(RM) -f src/*.o *.a test/run-tests$(E) test/run-benchmarks$(E) + +distclean: distclean-platform + $(RM) -f src/*.o *.a test/run-tests$(E) test/run-benchmarks$(E) diff --git a/src/rt/libuv/README b/src/rt/libuv/README deleted file mode 100644 index 4e2c2cd2614..00000000000 --- a/src/rt/libuv/README +++ /dev/null @@ -1,16 +0,0 @@ -This is the new networking layer for Node. Its purpose is to abstract -IOCP on windows and libev on Unix systems. We intend to eventually contain -all platform differences in this library. - -http://nodejs.org/ - -(This was previously called liboio) - -Supported Platforms: - -Microsoft Windows operating systems since Windows XP sp2. It can be built -with either Visual Studio or MinGW. - -Linux 2.6 and MacOS using the GCC toolchain. - -Solaris 121 and later using GCC toolchain. diff --git a/src/rt/libuv/README.md b/src/rt/libuv/README.md new file mode 100644 index 00000000000..9fc4ae62ebd --- /dev/null +++ b/src/rt/libuv/README.md @@ -0,0 +1,92 @@ +# libuv + +libuv is a new platform layer for Node. Its purpose is to abstract IOCP on +windows and libev on Unix systems. We intend to eventually contain all +platform differences in this library. + +http://nodejs.org/ + +## Features + +Implemented: + + * Non-blocking TCP sockets + + * Non-blocking named pipes + + * UDP + + * Timers + + * Child process spawning + + * Asynchronous DNS via c-ares or `uv_getaddrinfo`. + + * Asynchronous file system APIs `uv_fs_*` + + * High resolution time `uv_hrtime` + + * Current executable path look up `uv_exepath` + + * Thread pool scheduling `uv_queue_work` + +In-progress: + + * File system events (Currently supports inotify, `ReadDirectoryChangesW` + and will support kqueue and event ports in the near future.) + `uv_fs_event_t` + + * VT100 TTY `uv_tty_t` + + * Socket sharing between processes `uv_ipc_t` + + +## Documentation + +See `include/uv.h`. + + +## Build Instructions + +For GCC (including MinGW) there are two methods building: via normal +makefiles or via GYP. GYP is a meta-build system which can generate MSVS, +Makefile, and XCode backends. It is best used for integration into other +projects. The old (more stable) system is using Makefiles. + +To build via Makefile simply execute: + + make + +To build with Visual Studio run the vcbuilds.bat file which will +checkout the GYP code into build/gyp and generate the uv.sln and +related files. + +Windows users can also build from cmd-line using msbuild. This is +done by running vcbuild.bat from Visual Studio command prompt. + +To have GYP generate build script for another system you will need to +checkout GYP into the project tree manually: + + svn co http://gyp.googlecode.com/svn/trunk build/gyp + +Unix users run + + ./gyp_uv -f make + make + +Macintosh users run + + ./gyp_uv -f xcode + xcodebuild -project uv.xcodeproj -configuration Release -target All + + +## Supported Platforms + +Microsoft Windows operating systems since Windows XP SP2. It can be built +with either Visual Studio or MinGW. + +Linux 2.6 using the GCC toolchain. + +MacOS using the GCC or XCode toolchain. + +Solaris 121 and later using GCC toolchain. diff --git a/src/rt/libuv/common.gypi b/src/rt/libuv/common.gypi new file mode 100644 index 00000000000..9a0be4dc394 --- /dev/null +++ b/src/rt/libuv/common.gypi @@ -0,0 +1,164 @@ +{ + 'variables': { + 'visibility%': 'hidden', # V8's visibility setting + 'target_arch%': 'ia32', # set v8's target architecture + 'host_arch%': 'ia32', # set v8's host architecture + 'library%': 'static_library', # allow override to 'shared_library' for DLL/.so builds + 'component%': 'static_library', # NB. these names match with what V8 expects + 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way + }, + + 'target_defaults': { + 'default_configuration': 'Debug', + 'configurations': { + 'Debug': { + 'defines': [ 'DEBUG', '_DEBUG' ], + 'cflags': [ '-g', '-O0' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'target_conditions': [ + ['library=="static_library"', { + 'RuntimeLibrary': 1, # static debug + }, { + 'RuntimeLibrary': 3, # DLL debug + }], + ], + 'Optimization': 0, # /Od, no optimization + 'MinimalRebuild': 'true', + 'OmitFramePointers': 'false', + 'BasicRuntimeChecks': 3, # /RTC1 + }, + 'VCLinkerTool': { + 'LinkIncremental': 2, # enable incremental linking + }, + }, + }, + 'Release': { + 'defines': [ 'NDEBUG' ], + 'cflags': [ '-O3', '-fomit-frame-pointer', '-fdata-sections', '-ffunction-sections' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'target_conditions': [ + ['library=="static_library"', { + 'RuntimeLibrary': 0, # static release + }, { + 'RuntimeLibrary': 2, # debug release + }], + ], + 'Optimization': 3, # /Ox, full optimization + 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size + 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible + 'WholeProgramOptimization': 'true', # /GL, whole program optimization, needed for LTCG + 'OmitFramePointers': 'true', + 'EnableFunctionLevelLinking': 'true', + 'EnableIntrinsicFunctions': 'true', + 'AdditionalOptions': [ + '/MP', # compile across multiple CPUs + ], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': [ + '/LTCG', # link time code generation + ], + }, + 'VCLinkerTool': { + 'LinkTimeCodeGeneration': 1, # link-time code generation + 'OptimizeReferences': 2, # /OPT:REF + 'EnableCOMDATFolding': 2, # /OPT:ICF + 'LinkIncremental': 1, # disable incremental linking + }, + }, + } + }, + 'msvs_settings': { + 'VCCLCompilerTool': { + 'StringPooling': 'true', # pool string literals + 'DebugInformationFormat': 3, # Generate a PDB + 'WarningLevel': 3, + 'BufferSecurityCheck': 'true', + 'ExceptionHandling': 1, # /EHsc + 'SuppressStartupBanner': 'true', + 'WarnAsError': 'false', + }, + 'VCLibrarianTool': { + }, + 'VCLinkerTool': { + 'GenerateDebugInformation': 'true', + 'RandomizedBaseAddress': 2, # enable ASLR + 'DataExecutionPrevention': 2, # enable DEP + 'AllowIsolation': 'true', + 'SuppressStartupBanner': 'true', + 'target_conditions': [ + ['_type=="executable"', { + 'SubSystem': 1, # console executable + }], + ], + }, + }, + 'conditions': [ + ['OS == "win"', { + 'msvs_cygwin_shell': 0, # prevent actions from trying to use cygwin + 'defines': [ + 'WIN32', + # we don't really want VC++ warning us about + # how dangerous C functions are... + '_CRT_SECURE_NO_DEPRECATE', + # ... or that C implementations shouldn't use + # POSIX names + '_CRT_NONSTDC_NO_DEPRECATE', + ], + }], + [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { + 'cflags': [ '-Wall', '-pthread', ], + 'cflags_cc': [ '-fno-rtti', '-fno-exceptions' ], + 'ldflags': [ '-pthread', ], + 'conditions': [ + [ 'host_arch != target_arch and target_arch=="ia32"', { + 'cflags': [ '-m32' ], + 'ldflags': [ '-m32' ], + }], + [ 'OS=="linux"', { + 'cflags': [ '-ansi' ], + }], + [ 'visibility=="hidden"', { + 'cflags': [ '-fvisibility=hidden' ], + }], + ], + }], + ['OS=="mac"', { + 'xcode_settings': { + 'ALWAYS_SEARCH_USER_PATHS': 'NO', + 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks + 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic + # (Equivalent to -fPIC) + 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions + 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti + 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings + # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden + 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics + 'GCC_VERSION': '4.2', + 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof + 'MACOSX_DEPLOYMENT_TARGET': '10.4', # -mmacosx-version-min=10.4 + 'PREBINDING': 'NO', # No -Wl,-prebind + 'USE_HEADERMAP': 'NO', + 'OTHER_CFLAGS': [ + '-fno-strict-aliasing', + ], + 'WARNING_CFLAGS': [ + '-Wall', + '-Wendif-labels', + '-W', + '-Wno-unused-parameter', + ], + }, + 'target_conditions': [ + ['_type!="static_library"', { + 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']}, + }], + ], + }], + ], + }, +} diff --git a/src/rt/libuv/config-mingw.mk b/src/rt/libuv/config-mingw.mk index 7c975359af9..d4daca8a1ff 100644 --- a/src/rt/libuv/config-mingw.mk +++ b/src/rt/libuv/config-mingw.mk @@ -20,37 +20,41 @@ # Use make -f Makefile.gcc PREFIX=i686-w64-mingw32- # for cross compilation -CC ?= $(PREFIX)gcc -AR ?= $(PREFIX)ar +CC = $(PREFIX)gcc +AR = $(PREFIX)ar E=.exe -CFLAGS+=$(CPPFLAGS) -g --std=gnu89 -D_WIN32_WINNT=0x0501 -I$(S)/src/ares/config_win32 +CFLAGS=$(CPPFLAGS) -g --std=gnu89 -D_WIN32_WINNT=0x0501 -Isrc/ares/config_win32 LINKFLAGS=-lm CARES_OBJS += src/ares/windows_port.o +WIN_SRCS=$(wildcard src/win/*.c) +WIN_OBJS=$(WIN_SRCS:.c=.o) -uv.a: src/uv-win.o src/uv-common.o src/uv-eio.o src/eio/eio.o $(CARES_OBJS) - @$(call EE, ar: $@) - $(Q)$(AR) rcs uv.a $^ +RUNNER_CFLAGS=$(CFLAGS) -D_GNU_SOURCE # Need _GNU_SOURCE for strdup? +RUNNER_LINKFLAGS=$(LINKFLAGS) +RUNNER_LIBS=-lws2_32 +RUNNER_SRC=test/runner-win.c -src/uv-win.o: src/uv-win.c include/uv.h include/uv-win.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CFLAGS) -c $< -o $@ +uv.a: $(WIN_OBJS) src/uv-common.o $(CARES_OBJS) + $(AR) rcs uv.a src/win/*.o src/uv-common.o $(CARES_OBJS) -src/uv-common.o: src/uv-common.c include/uv.h include/uv-win.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CFLAGS) -c $< -o $@ +src/win/%.o: src/win/%.c src/win/internal.h + $(CC) $(CFLAGS) -o $@ -c $< + +src/uv-common.o: src/uv-common.c include/uv.h include/uv-private/uv-win.h + $(CC) $(CFLAGS) -c src/uv-common.c -o src/uv-common.o EIO_CPPFLAGS += $(CPPFLAGS) -EIO_CPPFLAGS += -DEIO_CONFIG_H=\"$(EIO_CONFIG)\" EIO_CPPFLAGS += -DEIO_STACKSIZE=65536 EIO_CPPFLAGS += -D_GNU_SOURCE -src/eio/eio.o: src/eio/eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(EIO_CPPFLAGS) $(CFLAGS) -c $< -o $@ - -src/uv-eio.o: src/uv-eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(CPPFLAGS) -I$(S)src/eio/ $(CFLAGS) -c $< -o $@ +clean-platform: + -rm -f src/ares/*.o + -rm -f src/eio/*.o + -rm -f src/win/*.o +distclean-platform: + -rm -f src/ares/*.o + -rm -f src/eio/*.o + -rm -f src/win/*.o diff --git a/src/rt/libuv/config-unix.mk b/src/rt/libuv/config-unix.mk index afdb92431c8..59bf8f17f8f 100644 --- a/src/rt/libuv/config-unix.mk +++ b/src/rt/libuv/config-unix.mk @@ -18,95 +18,127 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -CC ?= $(PREFIX)gcc -AR ?= $(PREFIX)ar +CC = $(PREFIX)gcc +AR = $(PREFIX)ar E= -CSTDFLAG=--std=c89 -pedantic -CFLAGS+=-g -CPPFLAGS += -I$(S)/src/ev +CSTDFLAG=--std=c89 -pedantic -Wall -Wextra -Wno-unused-parameter +CFLAGS += -g +CPPFLAGS += -Isrc/unix/ev LINKFLAGS=-lm CPPFLAGS += -D_LARGEFILE_SOURCE CPPFLAGS += -D_FILE_OFFSET_BITS=64 +OBJS += src/unix/core.o +OBJS += src/unix/fs.o +OBJS += src/unix/cares.o +OBJS += src/unix/udp.o +OBJS += src/unix/error.o +OBJS += src/unix/process.o +OBJS += src/unix/tcp.o +OBJS += src/unix/pipe.o +OBJS += src/unix/tty.o +OBJS += src/unix/stream.o + ifeq (SunOS,$(uname_S)) EV_CONFIG=config_sunos.h EIO_CONFIG=config_sunos.h -CPPFLAGS += -I$(S)/src/ares/config_sunos +CPPFLAGS += -Isrc/ares/config_sunos -D__EXTENSIONS__ -D_XOPEN_SOURCE=500 LINKFLAGS+=-lsocket -lnsl -UV_OS_FILE=uv-sunos.c +OBJS += src/unix/sunos.o endif ifeq (Darwin,$(uname_S)) EV_CONFIG=config_darwin.h EIO_CONFIG=config_darwin.h -CPPFLAGS += -I$(S)/src/ares/config_darwin +CPPFLAGS += -Isrc/ares/config_darwin LINKFLAGS+=-framework CoreServices -UV_OS_FILE=uv-darwin.c +OBJS += src/unix/darwin.o endif ifeq (Linux,$(uname_S)) EV_CONFIG=config_linux.h EIO_CONFIG=config_linux.h -CSTDFLAG += -D_XOPEN_SOURCE=600 -CPPFLAGS += -I$(S)/src/ares/config_linux +CSTDFLAG += -D_GNU_SOURCE +CPPFLAGS += -Isrc/ares/config_linux LINKFLAGS+=-lrt -UV_OS_FILE=uv-linux.c +OBJS += src/unix/linux.o endif ifeq (FreeBSD,$(uname_S)) EV_CONFIG=config_freebsd.h EIO_CONFIG=config_freebsd.h -CPPFLAGS += -I$(S)/src/ares/config_freebsd +CPPFLAGS += -Isrc/ares/config_freebsd LINKFLAGS+= -UV_OS_FILE=uv-freebsd.c +OBJS += src/unix/freebsd.o +endif + +ifeq (NetBSD,$(uname_S)) +EV_CONFIG=config_netbsd.h +EIO_CONFIG=config_netbsd.h +CPPFLAGS += -Isrc/ares/config_netbsd +LINKFLAGS+= +OBJS += src/unix/netbsd.o endif ifneq (,$(findstring CYGWIN,$(uname_S))) EV_CONFIG=config_cygwin.h EIO_CONFIG=config_cygwin.h -CPPFLAGS += -I$(S)/src/ares/config_cygwin +# We drop the --std=c89, it hides CLOCK_MONOTONIC on cygwin +CSTDFLAG = -D_GNU_SOURCE +CPPFLAGS += -Isrc/ares/config_cygwin LINKFLAGS+= -UV_OS_FILE=uv-cygwin.c +OBJS += src/unix/cygwin.o endif # Need _GNU_SOURCE for strdup? RUNNER_CFLAGS=$(CFLAGS) -D_GNU_SOURCE +RUNNER_LINKFLAGS=$(LINKFLAGS) + +ifeq (SunOS,$(uname_S)) +RUNNER_LINKFLAGS += -pthreads +else +RUNNER_LINKFLAGS += -pthread +endif -RUNNER_LINKFLAGS=$(LINKFLAGS) -pthread RUNNER_LIBS= RUNNER_SRC=test/runner-unix.c -uv.a: src/uv-unix.o src/uv-common.o src/uv-platform.o src/ev/ev.o src/uv-eio.o src/eio/eio.o $(CARES_OBJS) - @$(call EE, ar: $@) - $(Q)$(AR) rcs uv.a $^ +uv.a: $(OBJS) src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS) + $(AR) rcs uv.a $(OBJS) src/uv-common.o src/unix/uv-eio.o src/unix/ev/ev.o src/unix/eio/eio.o $(CARES_OBJS) -src/uv-platform.o: src/$(UV_OS_FILE) include/uv.h include/uv-unix.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ +src/unix/%.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h + $(CC) $(CSTDFLAG) $(CPPFLAGS) -Isrc $(CFLAGS) -c $< -o $@ -src/uv-unix.o: src/uv-unix.c include/uv.h include/uv-unix.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CSTDFLAG) $(CPPFLAGS) -I$(S)/eio $(CFLAGS) -c $< -o $@ +src/uv-common.o: src/uv-common.c include/uv.h include/uv-private/uv-unix.h + $(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c src/uv-common.c -o src/uv-common.o -src/uv-common.o: src/uv-common.c include/uv.h include/uv-unix.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ - -src/ev/ev.o: src/ev/ev.c - @$(call EE, compile: $@) - $(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DEV_CONFIG_H=\"$(EV_CONFIG)\" +src/unix/ev/ev.o: src/unix/ev/ev.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c src/unix/ev/ev.c -o src/unix/ev/ev.o -DEV_CONFIG_H=\"$(EV_CONFIG)\" EIO_CPPFLAGS += $(CPPFLAGS) EIO_CPPFLAGS += -DEIO_CONFIG_H=\"$(EIO_CONFIG)\" -EIO_CPPFLAGS += -DEIO_STACKSIZE=65536 +EIO_CPPFLAGS += -DEIO_STACKSIZE=262144 EIO_CPPFLAGS += -D_GNU_SOURCE -src/eio/eio.o: src/eio/eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(EIO_CPPFLAGS) $(CFLAGS) -c $< -o $@ +src/unix/eio/eio.o: src/unix/eio/eio.c + $(CC) $(EIO_CPPFLAGS) $(CFLAGS) -c src/unix/eio/eio.c -o src/unix/eio/eio.o -src/uv-eio.o: src/uv-eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(CPPFLAGS) -I$(S)/src/eio/ $(CSTDFLAG) $(CFLAGS) -c $< -o $@ +src/unix/uv-eio.o: src/unix/uv-eio.c + $(CC) $(CPPFLAGS) -Isrc/unix/eio/ $(CSTDFLAG) $(CFLAGS) -c src/unix/uv-eio.c -o src/unix/uv-eio.o + + +clean-platform: + -rm -f src/ares/*.o + -rm -f src/unix/ev/*.o + -rm -f src/unix/eio/*.o + -rm -f src/unix/*.o + -rm -rf test/run-tests.dSYM run-benchmarks.dSYM + +distclean-platform: + -rm -f src/ares/*.o + -rm -f src/unix/ev/*.o + -rm -f src/unix/*.o + -rm -f src/unix/eio/*.o + -rm -rf test/run-tests.dSYM run-benchmarks.dSYM diff --git a/src/rt/libuv/doc/desired-api.md b/src/rt/libuv/doc/desired-api.md deleted file mode 100644 index 9faa57591e9..00000000000 --- a/src/rt/libuv/doc/desired-api.md +++ /dev/null @@ -1,159 +0,0 @@ -Warning: this is not actual API but desired API. - -# `uv_handle_t` - -This is the abstract base class of all types of handles. All handles have in -common: - -* When handles are initialized, the reference count to the event loop is - increased by one. - -* The user owns the `uv_handle_t` memory and is in charge of freeing it. - -* In order to free resources associated with a handle, one must `uv_close()` - and wait for the `uv_close_cb` callback. After the close callback has been - made, the user is allowed to the `uv_handle_t` object. - -* The `uv_close_cb` is always made directly off the event loop. That is, it - is not called from `uv_close()`. - - - -# `uv_tcp_server_t` - -A TCP server class that is a subclass of `uv_handle_t`. This can be bound to -an address and begin accepting new TCP sockets. - - int uv_bind4(uv_tcp_server_t* tcp_server, struct sockaddr_in* address); - int uv_bind6(uv_tcp_server_t* tcp_server, struct sockaddr_in6* address); - -Binds the TCP server to an address. The `address` can be created with -`uv_ip4_addr()`. Call this before `uv_listen()` - -Returns zero on success, -1 on failure. Errors in order of least-seriousness: - -* `UV_EADDRINUSE` There is already another socket bound to the specified - address. - -* `UV_EADDRNOTAVAIL` The `address` parameter is an IP address that is not - -* `UV_EINVAL` The server is already bound to an address. - -* `UV_EFAULT` Memory of `address` parameter is unintelligible. - - - int uv_listen(uv_tcp_server_t*, int backlog, uv_connection_cb cb); - -Begins listening for connections. The accept callback is level-triggered. - - - int uv_accept(uv_tcp_server_t* server, - uv_tcp_t* client); - -Accepts a connection. This should be called after the accept callback is -made. The `client` parameter should be uninitialized memory; `uv_accept` is -used instead of `uv_tcp_init` for server-side `uv_tcp_t` initialization. - -Return value 0 indicates success, -1 failure. Possible errors: - -* `UV_EAGAIN` There are no connections. Wait for the `uv_connection_cb` callback - to be called again. - -* `UV_EFAULT` The memory of either `server` is unintelligible. - - - -# `uv_stream_t` - -An abstract subclass of `uv_handle_t`. Streams represent something that -reads and/or writes data. Streams can be half or full-duplex. TCP sockets -are streams, files are streams with offsets. - - int uv_read_start(uv_stream_t* stream, - uv_alloc_cb alloc_cb, - uv_read_cb read_cb); - -Starts the stream reading continuously. The `alloc_cb` is used to allow the -user to implement various means of supplying the stream with buffers to -fill. The `read_cb` returns buffers to the user filled with data. - -Sometimes the buffers returned to the user do not contain data. This does -not indicate EOF as in other systems. EOF is made via the `uv_eof_cb` which -can be set like this `uv_set_eof_cb(stream, eof_cb);` - - - int uv_read_stop(uv_stream_t* stream); - -Stops reading from the stream. - - int uv_write_req_init(uv_write_req_t*, - uv_stream_t*, - uv_buf_t bufs[], - int butcnf); - -Initiates a write request on a stream. - - int uv_shutdown_req_init(uv_shutdown_req_t*, uv_stream_t*) - -Initiates a shutdown of outgoing data once the write queue drains. - - - -# `uv_tcp_t` - -The TCP handle class represents one endpoint of a duplex TCP stream. -`uv_tcp_t` is a subclass of `uv_stream_t`. A TCP handle can represent a -client side connection (one that has been used with uv_connect_req_init`) -or a server-side connection (one that was initialized with `uv_accept`) - - int uv_connect_req_init(uv_connect_req_t* req, - uv_tcp_t* socket, - struct sockaddr* addr); - -Initiates a request to open a connection. - - - -# `uv_req_t` - -Abstract class represents an asynchronous request. This is a subclass of `uv_handle_t`. - - -# `uv_connect_req_t` - -Subclass of `uv_req_t`. Represents a request for a TCP connection. Operates -on `uv_tcp_t` handles. Like other types of requests the `close_cb` indicates -completion of the request. - - int uv_connect_req_init(uv_connect_req_t* req, - uv_tcp_t* socket, - struct sockaddr* addr); - -Initializes the connection request. Returning 0 indicates success, -1 if -there was an error. The following values can be retrieved from -`uv_last_error` in the case of an error: - -* ??? - - -# `uv_shutdown_req_t` - -Subclass of `uv_req_t`. Represents an ongoing shutdown request. Once the -write queue of the parent `uv_stream_t` is drained, the outbound data -channel is shutdown. Once a shutdown request is initiated on a stream, the -stream will allow no more writes. - - int uv_shutdown_req_init(uv_shutdown_req_t*, - uv_stream_t* parent); - -Initializes the shutdown request. - - -# `uv_write_req_t` - - int uv_write_req_init(uv_write_req_t*, - uv_stream_t*, - uv_buf_t bufs[], - int butcnf); - -Initiates a write request on a stream. diff --git a/src/rt/libuv/gyp_uv b/src/rt/libuv/gyp_uv new file mode 100755 index 00000000000..a7a9689c265 --- /dev/null +++ b/src/rt/libuv/gyp_uv @@ -0,0 +1,60 @@ +#!/usr/bin/env python +import glob +import os +import shlex +import sys + +script_dir = os.path.dirname(__file__) +uv_root = os.path.normpath(script_dir) + +sys.path.insert(0, os.path.join(uv_root, 'build', 'gyp', 'pylib')) +import gyp + +# Directory within which we want all generated files (including Makefiles) +# to be written. +output_dir = os.path.join(os.path.abspath(uv_root), 'out') + +def run_gyp(args): + rc = gyp.main(args) + if rc != 0: + print 'Error running GYP' + sys.exit(rc) + +if __name__ == '__main__': + args = sys.argv[1:] + + # GYP bug. + # On msvs it will crash if it gets an absolute path. + # On Mac/make it will crash if it doesn't get an absolute path. + if sys.platform == 'win32': + args.append(os.path.join(uv_root, 'uv.gyp')) + common_fn = os.path.join(uv_root, 'common.gypi') + options_fn = os.path.join(uv_root, 'options.gypi') + else: + args.append(os.path.join(os.path.abspath(uv_root), 'uv.gyp')) + common_fn = os.path.join(os.path.abspath(uv_root), 'common.gypi') + options_fn = os.path.join(os.path.abspath(uv_root), 'options.gypi') + + if os.path.exists(common_fn): + args.extend(['-I', common_fn]) + + if os.path.exists(options_fn): + args.extend(['-I', options_fn]) + + args.append('--depth=' + uv_root) + + # There's a bug with windows which doesn't allow this feature. + if sys.platform != 'win32': + + # Tell gyp to write the Makefiles into output_dir + args.extend(['--generator-output', output_dir]) + + # Tell make to write its output into the same dir + args.extend(['-Goutput_dir=' + output_dir]) + + args.append('-Dtarget_arch=ia32') + args.append('-Dcomponent=static_library') + args.append('-Dlibrary=static_library') + gyp_args = list(args) + print gyp_args + run_gyp(gyp_args) diff --git a/src/rt/libuv/include/eio.h b/src/rt/libuv/include/uv-private/eio.h similarity index 78% rename from src/rt/libuv/include/eio.h rename to src/rt/libuv/include/uv-private/eio.h index 3dd3267678a..450df6ba299 100644 --- a/src/rt/libuv/include/eio.h +++ b/src/rt/libuv/include/uv-private/eio.h @@ -1,7 +1,7 @@ /* * libeio API header * - * Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann + * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- @@ -45,17 +45,9 @@ extern "C" { #endif #include +#include #include -#ifdef __OpenBSD__ -# include -#endif - -#ifdef _WIN32 -# define uid_t int -# define gid_t int -#endif - typedef struct eio_req eio_req; typedef struct eio_dirent eio_dirent; @@ -67,12 +59,35 @@ typedef int (*eio_cb)(eio_req *req); #ifndef EIO_STRUCT_STAT # ifdef _WIN32 -# define EIO_STRUCT_STAT struct _stati64 +# define EIO_STRUCT_STAT struct _stati64 +# define EIO_STRUCT_STATI64 # else -# define EIO_STRUCT_STAT struct stat +# define EIO_STRUCT_STAT struct stat # endif #endif +#ifdef _WIN32 + typedef int eio_uid_t; + typedef int eio_gid_t; + typedef int eio_mode_t; + #ifdef __MINGW32__ /* no intptr_t */ + typedef ssize_t eio_ssize_t; + #else + typedef intptr_t eio_ssize_t; /* or SSIZE_T */ + #endif + #if __GNUC__ + typedef long long eio_ino_t; + #else + typedef __int64 eio_ino_t; /* unsigned not supported by msvc */ + #endif +#else + typedef uid_t eio_uid_t; + typedef gid_t eio_gid_t; + typedef ssize_t eio_ssize_t; + typedef ino_t eio_ino_t; + typedef mode_t eio_mode_t; +#endif + #ifndef EIO_STRUCT_STATVFS # define EIO_STRUCT_STATVFS struct statvfs #endif @@ -119,7 +134,7 @@ struct eio_dirent unsigned short namelen; /* size of filename without trailing 0 */ unsigned char type; /* one of EIO_DT_* */ signed char score; /* internal use */ - ino_t inode; /* the inode number, if available, otherwise unspecified */ + eio_ino_t inode; /* the inode number, if available, otherwise unspecified */ }; /* eio_msync flags */ @@ -131,14 +146,12 @@ enum }; /* eio_mtouch flags */ - enum { EIO_MT_MODIFY = 1 }; /* eio_sync_file_range flags */ - enum { EIO_SYNC_FILE_RANGE_WAIT_BEFORE = 1, @@ -146,10 +159,16 @@ enum EIO_SYNC_FILE_RANGE_WAIT_AFTER = 4 }; -typedef double eio_tstamp; /* feel free to use double in your code directly */ +/* eio_fallocate flags */ +enum +{ + EIO_FALLOC_FL_KEEP_SIZE = 1 /* MUST match the value in linux/falloc.h */ +}; + +/* timestamps and differences - feel free to use double in your code directly */ +typedef double eio_tstamp; /* the eio request structure */ - enum { EIO_CUSTOM, @@ -162,12 +181,12 @@ enum EIO_UTIME, EIO_FUTIME, EIO_CHMOD, EIO_FCHMOD, EIO_CHOWN, EIO_FCHOWN, - EIO_SYNC, EIO_FSYNC, EIO_FDATASYNC, - EIO_MSYNC, EIO_MTOUCH, EIO_SYNC_FILE_RANGE, + EIO_SYNC, EIO_FSYNC, EIO_FDATASYNC, EIO_SYNCFS, + EIO_MSYNC, EIO_MTOUCH, EIO_SYNC_FILE_RANGE, EIO_FALLOCATE, EIO_MLOCK, EIO_MLOCKALL, EIO_UNLINK, EIO_RMDIR, EIO_MKDIR, EIO_RENAME, EIO_MKNOD, EIO_READDIR, - EIO_LINK, EIO_SYMLINK, EIO_READLINK, + EIO_LINK, EIO_SYMLINK, EIO_READLINK, EIO_REALPATH, EIO_GROUP, EIO_NOP, EIO_BUSY }; @@ -194,9 +213,9 @@ struct eio_req { eio_req volatile *next; /* private ETP */ - ssize_t result; /* result of syscall, e.g. result = read (... */ - off_t offs; /* read, write, truncate, readahead, sync_file_range: file offset */ - size_t size; /* read, write, readahead, sendfile, msync, mlock, sync_file_range: length */ + eio_ssize_t result; /* result of syscall, e.g. result = read (... */ + off_t offs; /* read, write, truncate, readahead, sync_file_range, fallocate: file offset, mknod: dev_t */ + size_t size; /* read, write, readahead, sendfile, msync, mlock, sync_file_range, fallocate: length */ void *ptr1; /* all applicable requests: pathname, old name; readdir: optional eio_dirents */ void *ptr2; /* all applicable requests: new name or memory buffer; readdir: name strings */ eio_tstamp nv1; /* utime, futime: atime; busy: sleep time */ @@ -204,16 +223,22 @@ struct eio_req int type; /* EIO_xxx constant ETP */ int int1; /* all applicable requests: file descriptor; sendfile: output fd; open, msync, mlockall, readdir: flags */ - long int2; /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, sync_file_range: flags */ - long int3; /* chown, fchown: gid; mknod: dev_t */ + long int2; /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, sync_file_range, fallocate: flags */ + long int3; /* chown, fchown: gid */ int errorno; /* errno value on syscall return */ +#if __i386 || __amd64 + unsigned char cancelled; +#else + sig_atomic_t cancelled; +#endif + unsigned char flags; /* private */ signed char pri; /* the priority */ void *data; eio_cb finish; - void (*destroy)(eio_req *req); /* called when requets no longer needed */ + void (*destroy)(eio_req *req); /* called when request no longer needed */ void (*feed)(eio_req *req); /* only used for group requests */ EIO_REQ_MEMBERS @@ -223,10 +248,9 @@ struct eio_req /* _private_ request flags */ enum { - EIO_FLAG_CANCELLED = 0x01, /* request was cancelled */ - EIO_FLAG_PTR1_FREE = 0x02, /* need to free(ptr1) */ - EIO_FLAG_PTR2_FREE = 0x04, /* need to free(ptr2) */ - EIO_FLAG_GROUPADD = 0x08 /* some request was added to the group */ + EIO_FLAG_PTR1_FREE = 0x01, /* need to free(ptr1) */ + EIO_FLAG_PTR2_FREE = 0x02, /* need to free(ptr2) */ + EIO_FLAG_GROUPADD = 0x04 /* some request was added to the group */ }; /* undocumented/unsupported/private helper */ @@ -254,14 +278,15 @@ void eio_set_max_poll_reqs (unsigned int nreqs); void eio_set_min_parallel (unsigned int nthreads); void eio_set_max_parallel (unsigned int nthreads); void eio_set_max_idle (unsigned int nthreads); +void eio_set_idle_timeout (unsigned int seconds); unsigned int eio_nreqs (void); /* number of requests in-flight */ unsigned int eio_nready (void); /* number of not-yet handled requests */ -unsigned int eio_npending (void); /* numbe rof finished but unhandled requests */ +unsigned int eio_npending (void); /* number of finished but unhandled requests */ unsigned int eio_nthreads (void); /* number of worker threads in use currently */ /*****************************************************************************/ -/* convinience wrappers */ +/* convenience wrappers */ #ifndef EIO_NO_WRAPPERS eio_req *eio_nop (int pri, eio_cb cb, void *data); /* does nothing except go through the whole process */ @@ -269,11 +294,13 @@ eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data); /* ti eio_req *eio_sync (int pri, eio_cb cb, void *data); eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data); eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data); +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data); eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data); eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data); eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data); +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data); eio_req *eio_close (int fd, int pri, eio_cb cb, void *data); eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data); eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); @@ -282,28 +309,29 @@ eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data); /* stat buffer= eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data); +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data); eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data); eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data); -eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data); eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data); eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data); +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data); eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data); eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data); eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_custom (eio_cb execute, int pri, eio_cb cb, void *data); +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data); #endif /*****************************************************************************/ @@ -319,7 +347,7 @@ void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the g /* request api */ /* true if the request was cancelled, useful in the invoke callback */ -#define EIO_CANCELLED(req) ((req)->flags & EIO_FLAG_CANCELLED) +#define EIO_CANCELLED(req) ((req)->cancelled) #define EIO_RESULT(req) ((req)->result) /* returns a pointer to the result buffer allocated by eio */ @@ -332,21 +360,13 @@ void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the g void eio_submit (eio_req *req); /* cancel a request as soon fast as possible, if possible */ void eio_cancel (eio_req *req); -/* destroy a request that has never been submitted */ -void eio_destroy (eio_req *req); /*****************************************************************************/ -/* convinience functions */ +/* convenience functions */ -ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count); - -/*****************************************************************************/ -/* export these so node_file can use these function instead of pread/write */ - -#if !HAVE_PREADWRITE -ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset); -ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset); -#endif +eio_ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count); +eio_ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset); +eio_ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset); #ifdef __cplusplus } diff --git a/src/rt/libuv/include/ev.h b/src/rt/libuv/include/uv-private/ev.h similarity index 96% rename from src/rt/libuv/include/ev.h rename to src/rt/libuv/include/uv-private/ev.h index 526b9f1bcb9..1db1e266493 100644 --- a/src/rt/libuv/include/ev.h +++ b/src/rt/libuv/include/uv-private/ev.h @@ -48,6 +48,12 @@ EV_CPP(extern "C" {) +#ifdef __GNUC__ +# define EV_MAYBE_UNUSED __attribute__ ((unused)) +#else +# define EV_MAYBE_UNUSED +#endif + /*****************************************************************************/ /* pre-4.0 compatibility */ @@ -539,7 +545,7 @@ void ev_set_syserr_cb (void (*cb)(const char *msg)); struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)); EV_INLINE struct ev_loop * -ev_default_loop_uc_ (void) +EV_MAYBE_UNUSED ev_default_loop_uc_ (void) { extern struct ev_loop *ev_default_loop_ptr; @@ -547,7 +553,7 @@ ev_default_loop_uc_ (void) } EV_INLINE int -ev_is_default_loop (EV_P) +EV_MAYBE_UNUSED ev_is_default_loop (EV_P) { return EV_A == EV_DEFAULT_UC; } @@ -807,14 +813,14 @@ void ev_async_send (EV_P_ ev_async *w); #define EVUNLOOP_ONE EVBREAK_ONE #define EVUNLOOP_ALL EVBREAK_ALL #if EV_PROTOTYPES - EV_INLINE void ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); } - EV_INLINE void ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); } - EV_INLINE void ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); } - EV_INLINE void ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); } + EV_INLINE void EV_MAYBE_UNUSED ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); } + EV_INLINE void EV_MAYBE_UNUSED ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); } + EV_INLINE void EV_MAYBE_UNUSED ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); } + EV_INLINE void EV_MAYBE_UNUSED ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); } #if EV_FEATURE_API - EV_INLINE unsigned int ev_loop_count (EV_P) { return ev_iteration (EV_A); } - EV_INLINE unsigned int ev_loop_depth (EV_P) { return ev_depth (EV_A); } - EV_INLINE void ev_loop_verify (EV_P) { ev_verify (EV_A); } + EV_INLINE unsigned int EV_MAYBE_UNUSED ev_loop_count (EV_P) { return ev_iteration (EV_A); } + EV_INLINE unsigned int EV_MAYBE_UNUSED ev_loop_depth (EV_P) { return ev_depth (EV_A); } + EV_INLINE void EV_MAYBE_UNUSED ev_loop_verify (EV_P) { ev_verify (EV_A); } #endif #endif #else diff --git a/src/rt/libuv/include/ngx-queue.h b/src/rt/libuv/include/uv-private/ngx-queue.h similarity index 100% rename from src/rt/libuv/include/ngx-queue.h rename to src/rt/libuv/include/uv-private/ngx-queue.h diff --git a/src/rt/libuv/include/tree.h b/src/rt/libuv/include/uv-private/tree.h similarity index 100% rename from src/rt/libuv/include/tree.h rename to src/rt/libuv/include/uv-private/tree.h diff --git a/src/rt/libuv/src/uv-freebsd.c b/src/rt/libuv/include/uv-private/uv-linux.h similarity index 73% rename from src/rt/libuv/src/uv-freebsd.c rename to src/rt/libuv/include/uv-private/uv-linux.h index 108455d8804..7f7b05932d9 100644 --- a/src/rt/libuv/src/uv-freebsd.c +++ b/src/rt/libuv/include/uv-private/uv-linux.h @@ -1,4 +1,5 @@ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the @@ -18,32 +19,11 @@ * IN THE SOFTWARE. */ -#include "uv.h" +#ifndef UV_LINUX_H +#define UV_LINUX_H +#define UV_FS_EVENT_PRIVATE_FIELDS \ + ev_io read_watcher; \ + uv_fs_event_cb cb; \ -int uv_exepath(char* buffer, size_t* size) { - uint32_t usize; - int result; - char* path; - char* fullpath; - - if (!buffer || !size) { - return -1; - } - - int mib[4]; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PATHNAME; - mib[3] = -1; - - size_t cb = *size; - if (sysctl(mib, 4, buffer, &cb, NULL, 0) < 0) { - *size = 0; - return -1; - } - *size = strlen(buffer); - - return 0; -} +#endif /* UV_LINUX_H */ diff --git a/src/rt/libuv/include/uv-unix.h b/src/rt/libuv/include/uv-private/uv-unix.h similarity index 59% rename from src/rt/libuv/include/uv-unix.h rename to src/rt/libuv/include/uv-private/uv-unix.h index f68a7fd6fd9..5fa132872dc 100644 --- a/src/rt/libuv/include/uv-unix.h +++ b/src/rt/libuv/include/uv-private/uv-unix.h @@ -25,6 +25,11 @@ #include "ngx-queue.h" #include "ev.h" +#include "eio.h" + +#if defined(__linux__) +#include "uv-private/uv-linux.h" +#endif #include #include @@ -39,16 +44,52 @@ typedef struct { size_t len; } uv_buf_t; +typedef int uv_file; + +/* Stub. Remove it once all platforms support the file watcher API. */ +#ifndef UV_FS_EVENT_PRIVATE_FIELDS +#define UV_FS_EVENT_PRIVATE_FIELDS /* empty */ +#endif + +#define UV_LOOP_PRIVATE_FIELDS \ + ares_channel channel; \ + /* \ + * While the channel is active this timer is called once per second to be \ + * sure that we're always calling ares_process. See the warning above the \ + * definition of ares_timeout(). \ + */ \ + ev_timer timer; \ + struct ev_loop* ev; + #define UV_REQ_BUFSML_SIZE (4) -#define UV_REQ_PRIVATE_FIELDS \ - int write_index; \ - ev_timer timer; \ +#define UV_REQ_PRIVATE_FIELDS /* empty */ + +#define UV_WRITE_PRIVATE_FIELDS \ ngx_queue_t queue; \ + int write_index; \ uv_buf_t* bufs; \ int bufcnt; \ + int error; \ uv_buf_t bufsml[UV_REQ_BUFSML_SIZE]; +#define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */ + +#define UV_CONNECT_PRIVATE_FIELDS \ + ngx_queue_t queue; + +#define UV_UDP_SEND_PRIVATE_FIELDS \ + ngx_queue_t queue; \ + struct sockaddr_storage addr; \ + socklen_t addrlen; \ + uv_buf_t* bufs; \ + int bufcnt; \ + ssize_t status; \ + uv_udp_send_cb send_cb; \ + uv_buf_t bufsml[UV_REQ_BUFSML_SIZE]; \ + +#define UV_PRIVATE_REQ_TYPES /* empty */ + /* TODO: union or classes please! */ #define UV_HANDLE_PRIVATE_FIELDS \ @@ -59,20 +100,35 @@ typedef struct { #define UV_STREAM_PRIVATE_FIELDS \ uv_read_cb read_cb; \ - uv_alloc_cb alloc_cb; - - -/* UV_TCP */ -#define UV_TCP_PRIVATE_FIELDS \ - int delayed_error; \ - uv_connection_cb connection_cb; \ - int accepted_fd; \ - uv_req_t *connect_req; \ - uv_req_t *shutdown_req; \ + uv_alloc_cb alloc_cb; \ + uv_connect_t *connect_req; \ + uv_shutdown_t *shutdown_req; \ ev_io read_watcher; \ ev_io write_watcher; \ ngx_queue_t write_queue; \ - ngx_queue_t write_completed_queue; + ngx_queue_t write_completed_queue; \ + int delayed_error; \ + uv_connection_cb connection_cb; \ + int accepted_fd; + + +/* UV_TCP */ +#define UV_TCP_PRIVATE_FIELDS + + +/* UV_UDP */ +#define UV_UDP_PRIVATE_FIELDS \ + uv_alloc_cb alloc_cb; \ + uv_udp_recv_cb recv_cb; \ + ev_io read_watcher; \ + ev_io write_watcher; \ + ngx_queue_t write_queue; \ + ngx_queue_t write_completed_queue; \ + + +/* UV_NAMED_PIPE */ +#define UV_PIPE_PRIVATE_FIELDS \ + const char* pipe_fname; /* strdup'ed */ /* UV_PREPARE */ \ @@ -117,4 +173,16 @@ typedef struct { struct addrinfo* res; \ int retcode; +#define UV_PROCESS_PRIVATE_FIELDS \ + ev_child child_watcher; + +#define UV_FS_PRIVATE_FIELDS \ + struct stat statbuf; \ + eio_req* eio; + +#define UV_WORK_PRIVATE_FIELDS \ + eio_req* eio; + +#define UV_TTY_PRIVATE_FIELDS /* empty */ + #endif /* UV_UNIX_H */ diff --git a/src/rt/libuv/include/uv-private/uv-win.h b/src/rt/libuv/include/uv-private/uv-win.h new file mode 100644 index 00000000000..f203d672891 --- /dev/null +++ b/src/rt/libuv/include/uv-private/uv-win.h @@ -0,0 +1,281 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0502 +#endif + +#include +#include +#include +#include +#include +#include + +#include "tree.h" + +#define MAX_PIPENAME_LEN 256 + +/** + * It should be possible to cast uv_buf_t[] to WSABUF[] + * see http://msdn.microsoft.com/en-us/library/ms741542(v=vs.85).aspx + */ +typedef struct uv_buf_t { + ULONG len; + char* base; +} uv_buf_t; + +typedef int uv_file; + +RB_HEAD(uv_timer_tree_s, uv_timer_s); + +#define UV_LOOP_PRIVATE_FIELDS \ + /* The loop's I/O completion port */ \ + HANDLE iocp; \ + /* Reference count that keeps the event loop alive */ \ + int refs; \ + /* The current time according to the event loop. in msecs. */ \ + int64_t time; \ + /* Tail of a single-linked circular queue of pending reqs. If the queue */ \ + /* is empty, tail_ is NULL. If there is only one item, */ \ + /* tail_->next_req == tail_ */ \ + uv_req_t* pending_reqs_tail; \ + /* Head of a single-linked list of closed handles */ \ + uv_handle_t* endgame_handles; \ + /* The head of the timers tree */ \ + struct uv_timer_tree_s timers; \ + /* Lists of active loop (prepare / check / idle) watchers */ \ + uv_prepare_t* prepare_handles; \ + uv_check_t* check_handles; \ + uv_idle_t* idle_handles; \ + /* This pointer will refer to the prepare/check/idle handle whose */ \ + /* callback is scheduled to be called next. This is needed to allow */ \ + /* safe removal from one of the lists above while that list being */ \ + /* iterated over. */ \ + uv_prepare_t* next_prepare_handle; \ + uv_check_t* next_check_handle; \ + uv_idle_t* next_idle_handle; \ + ares_channel ares_chan; \ + int ares_active_sockets; \ + uv_timer_t ares_polling_timer; \ + /* Last error code */ \ + uv_err_t last_error; + +#define UV_REQ_TYPE_PRIVATE \ + /* TODO: remove the req suffix */ \ + UV_ARES_EVENT_REQ, \ + UV_ARES_CLEANUP_REQ, \ + UV_GETADDRINFO_REQ, \ + UV_PROCESS_EXIT, \ + UV_PROCESS_CLOSE, \ + UV_UDP_RECV, \ + UV_FS_EVENT_REQ + +#define UV_REQ_PRIVATE_FIELDS \ + union { \ + /* Used by I/O operations */ \ + struct { \ + OVERLAPPED overlapped; \ + size_t queued_bytes; \ + }; \ + }; \ + struct uv_req_s* next_req; + +#define UV_WRITE_PRIVATE_FIELDS \ + /* empty */ + +#define UV_CONNECT_PRIVATE_FIELDS \ + /* empty */ + +#define UV_SHUTDOWN_PRIVATE_FIELDS \ + /* empty */ + +#define UV_UDP_SEND_PRIVATE_FIELDS \ + /* empty */ + +#define UV_PRIVATE_REQ_TYPES \ + typedef struct uv_pipe_accept_s { \ + UV_REQ_FIELDS \ + HANDLE pipeHandle; \ + struct uv_pipe_accept_s* next_pending; \ + } uv_pipe_accept_t; \ + typedef struct uv_tcp_accept_s { \ + UV_REQ_FIELDS \ + SOCKET accept_socket; \ + char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; \ + struct uv_tcp_accept_s* next_pending; \ + } uv_tcp_accept_t; + +#define uv_stream_connection_fields \ + unsigned int write_reqs_pending; \ + uv_shutdown_t* shutdown_req; + +#define uv_stream_server_fields \ + uv_connection_cb connection_cb; + +#define UV_STREAM_PRIVATE_FIELDS \ + unsigned int reqs_pending; \ + uv_alloc_cb alloc_cb; \ + uv_read_cb read_cb; \ + uv_req_t read_req; \ + union { \ + struct { uv_stream_connection_fields }; \ + struct { uv_stream_server_fields }; \ + }; + +#define uv_tcp_server_fields \ + uv_tcp_accept_t* accept_reqs; \ + uv_tcp_accept_t* pending_accepts; + +#define uv_tcp_connection_fields \ + uv_buf_t read_buffer; + +#define UV_TCP_PRIVATE_FIELDS \ + SOCKET socket; \ + uv_err_t bind_error; \ + union { \ + struct { uv_tcp_server_fields }; \ + struct { uv_tcp_connection_fields }; \ + }; + +#define UV_UDP_PRIVATE_FIELDS \ + SOCKET socket; \ + unsigned int reqs_pending; \ + uv_req_t recv_req; \ + uv_buf_t recv_buffer; \ + struct sockaddr_storage recv_from; \ + int recv_from_len; \ + uv_udp_recv_cb recv_cb; \ + uv_alloc_cb alloc_cb; + +#define uv_pipe_server_fields \ + uv_pipe_accept_t accept_reqs[4]; \ + uv_pipe_accept_t* pending_accepts; + +#define uv_pipe_connection_fields \ + uv_timer_t* eof_timer; + +#define UV_PIPE_PRIVATE_FIELDS \ + HANDLE handle; \ + wchar_t* name; \ + union { \ + struct { uv_pipe_server_fields }; \ + struct { uv_pipe_connection_fields }; \ + }; + +#define UV_TIMER_PRIVATE_FIELDS \ + RB_ENTRY(uv_timer_s) tree_entry; \ + int64_t due; \ + int64_t repeat; \ + uv_timer_cb timer_cb; + +#define UV_ASYNC_PRIVATE_FIELDS \ + struct uv_req_s async_req; \ + uv_async_cb async_cb; \ + /* char to avoid alignment issues */ \ + char volatile async_sent; + +#define UV_PREPARE_PRIVATE_FIELDS \ + uv_prepare_t* prepare_prev; \ + uv_prepare_t* prepare_next; \ + uv_prepare_cb prepare_cb; + +#define UV_CHECK_PRIVATE_FIELDS \ + uv_check_t* check_prev; \ + uv_check_t* check_next; \ + uv_check_cb check_cb; + +#define UV_IDLE_PRIVATE_FIELDS \ + uv_idle_t* idle_prev; \ + uv_idle_t* idle_next; \ + uv_idle_cb idle_cb; + +#define UV_HANDLE_PRIVATE_FIELDS \ + uv_handle_t* endgame_next; \ + unsigned int flags; + +#define UV_ARES_TASK_PRIVATE_FIELDS \ + struct uv_req_s ares_req; \ + SOCKET sock; \ + HANDLE h_wait; \ + WSAEVENT h_event; \ + HANDLE h_close_event; + +#define UV_GETADDRINFO_PRIVATE_FIELDS \ + struct uv_req_s getadddrinfo_req; \ + uv_getaddrinfo_cb getaddrinfo_cb; \ + void* alloc; \ + wchar_t* node; \ + wchar_t* service; \ + struct addrinfoW* hints; \ + struct addrinfoW* res; \ + int retcode; + +#define UV_PROCESS_PRIVATE_FIELDS \ + struct uv_process_exit_s { \ + UV_REQ_FIELDS \ + } exit_req; \ + struct uv_process_close_s { \ + UV_REQ_FIELDS \ + } close_req; \ + HANDLE child_stdio[3]; \ + int exit_signal; \ + DWORD spawn_errno; \ + HANDLE wait_handle; \ + HANDLE process_handle; \ + HANDLE close_handle; + +#define UV_FS_PRIVATE_FIELDS \ + int flags; \ + int last_error; \ + struct _stati64 stat; \ + void* arg0; \ + union { \ + struct { \ + void* arg1; \ + void* arg2; \ + void* arg3; \ + }; \ + struct { \ + ssize_t arg4; \ + ssize_t arg5; \ + }; \ + }; + +#define UV_WORK_PRIVATE_FIELDS \ + +#define UV_FS_EVENT_PRIVATE_FIELDS \ + struct uv_fs_event_req_s { \ + UV_REQ_FIELDS \ + } req; \ + HANDLE dir_handle; \ + int req_pending; \ + uv_fs_event_cb cb; \ + wchar_t* filew; \ + int is_path_dir; \ + char* buffer; + +#define UV_TTY_PRIVATE_FIELDS /* empty */ + +int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, + char* utf8Buffer, size_t utf8Size); +int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, + size_t utf16Size); diff --git a/src/rt/libuv/include/uv-win.h b/src/rt/libuv/include/uv-win.h deleted file mode 100644 index e6254fee09a..00000000000 --- a/src/rt/libuv/include/uv-win.h +++ /dev/null @@ -1,130 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0501 -#endif - -#include -#include -#include -#include -#include - -#include "tree.h" - -/** - * It should be possible to cast uv_buf_t[] to WSABUF[] - * see http://msdn.microsoft.com/en-us/library/ms741542(v=vs.85).aspx - */ -typedef struct uv_buf_t { - ULONG len; - char* base; -} uv_buf_t; - -#define UV_REQ_PRIVATE_FIELDS \ - union { \ - /* Used by I/O operations */ \ - struct { \ - OVERLAPPED overlapped; \ - size_t queued_bytes; \ - }; \ - }; \ - int flags; \ - uv_err_t error; \ - struct uv_req_s* next_req; - -#define UV_STREAM_PRIVATE_FIELDS \ - uv_alloc_cb alloc_cb; \ - uv_read_cb read_cb; \ - struct uv_req_s read_req; \ - -#define uv_tcp_connection_fields \ - unsigned int write_reqs_pending; \ - uv_req_t* shutdown_req; - -#define uv_tcp_server_fields \ - uv_connection_cb connection_cb; \ - SOCKET accept_socket; \ - struct uv_req_s accept_req; \ - char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; - -#define UV_TCP_PRIVATE_FIELDS \ - unsigned int reqs_pending; \ - union { \ - SOCKET socket; \ - HANDLE handle; \ - }; \ - union { \ - struct { uv_tcp_connection_fields }; \ - struct { uv_tcp_server_fields }; \ - }; - -#define UV_TIMER_PRIVATE_FIELDS \ - RB_ENTRY(uv_timer_s) tree_entry; \ - int64_t due; \ - int64_t repeat; \ - uv_timer_cb timer_cb; - -#define UV_ASYNC_PRIVATE_FIELDS \ - struct uv_req_s async_req; \ - /* char to avoid alignment issues */ \ - char volatile async_sent; - -#define UV_PREPARE_PRIVATE_FIELDS \ - uv_prepare_t* prepare_prev; \ - uv_prepare_t* prepare_next; \ - uv_prepare_cb prepare_cb; - -#define UV_CHECK_PRIVATE_FIELDS \ - uv_check_t* check_prev; \ - uv_check_t* check_next; \ - uv_check_cb check_cb; - -#define UV_IDLE_PRIVATE_FIELDS \ - uv_idle_t* idle_prev; \ - uv_idle_t* idle_next; \ - uv_idle_cb idle_cb; - -#define UV_HANDLE_PRIVATE_FIELDS \ - uv_handle_t* endgame_next; \ - unsigned int flags; \ - uv_err_t error; - -#define UV_ARES_TASK_PRIVATE_FIELDS \ - struct uv_req_s ares_req; \ - SOCKET sock; \ - HANDLE h_wait; \ - WSAEVENT h_event; \ - HANDLE h_close_event; - -#define UV_GETADDRINFO_PRIVATE_FIELDS \ - struct uv_req_s getadddrinfo_req; \ - uv_getaddrinfo_cb getaddrinfo_cb; \ - void* alloc; \ - wchar_t* node; \ - wchar_t* service; \ - struct addrinfoW* hints; \ - struct addrinfoW* res; \ - int retcode; - -int uv_utf16_to_utf8(wchar_t* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size); -int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, size_t utf16Size); diff --git a/src/rt/libuv/include/uv.h b/src/rt/libuv/include/uv.h index 6aecb282008..b8bdc093022 100644 --- a/src/rt/libuv/include/uv.h +++ b/src/rt/libuv/include/uv.h @@ -19,6 +19,8 @@ * IN THE SOFTWARE. */ +/* See uv_loop_new for an introduction. */ + #ifndef UV_H #define UV_H #ifdef __cplusplus @@ -39,27 +41,77 @@ extern "C" { typedef intptr_t ssize_t; #endif +typedef struct uv_loop_s uv_loop_t; +typedef struct uv_ares_task_s uv_ares_task_t; typedef struct uv_err_s uv_err_t; typedef struct uv_handle_s uv_handle_t; typedef struct uv_stream_s uv_stream_t; typedef struct uv_tcp_s uv_tcp_t; +typedef struct uv_udp_s uv_udp_t; +typedef struct uv_pipe_s uv_pipe_t; +typedef struct uv_tty_s uv_tty_t; typedef struct uv_timer_s uv_timer_t; typedef struct uv_prepare_s uv_prepare_t; typedef struct uv_check_s uv_check_t; typedef struct uv_idle_s uv_idle_t; -typedef struct uv_req_s uv_req_t; typedef struct uv_async_s uv_async_t; typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; - +typedef struct uv_process_s uv_process_t; +typedef struct uv_counters_s uv_counters_t; +/* Request types */ +typedef struct uv_req_s uv_req_t; +typedef struct uv_shutdown_s uv_shutdown_t; +typedef struct uv_write_s uv_write_t; +typedef struct uv_connect_s uv_connect_t; +typedef struct uv_udp_send_s uv_udp_send_t; +typedef struct uv_fs_s uv_fs_t; +/* uv_fs_event_t is a subclass of uv_handle_t. */ +typedef struct uv_fs_event_s uv_fs_event_t; +typedef struct uv_work_s uv_work_t; #if defined(__unix__) || defined(__POSIX__) || defined(__APPLE__) -# include "uv-unix.h" +# include "uv-private/uv-unix.h" #else -# include "uv-win.h" +# include "uv-private/uv-win.h" #endif -/* The status parameter is 0 if the request completed successfully, +/* + * This function must be called before any other functions in libuv. + * + * All functions besides uv_run() are non-blocking. + * + * All callbacks in libuv are made asynchronously. That is they are never + * made by the function that takes them as a parameter. + */ +uv_loop_t* uv_loop_new(); +void uv_loop_delete(uv_loop_t*); + + +/* + * Returns the default loop. + */ +uv_loop_t* uv_default_loop(); + +/* + * This function starts the event loop. It blocks until the reference count + * of the loop drops to zero. + */ +int uv_run(uv_loop_t*); + +/* + * Manually modify the event loop's reference count. Useful if the user wants + * to have a handle or timeout that doesn't keep the loop alive. + */ +void uv_ref(uv_loop_t*); +void uv_unref(uv_loop_t*); + +void uv_update_time(uv_loop_t*); +int64_t uv_now(uv_loop_t*); + + +/* + * The status parameter is 0 if the request completed successfully, * and should be -1 if the request was cancelled or failed. * For uv_close_cb, -1 means that the handle was closed due to an error. * Error details can be obtained by calling uv_last_error(). @@ -67,12 +119,12 @@ typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; * In the case of uv_read_cb the uv_buf_t returned should be freed by the * user. */ -typedef uv_buf_t (*uv_alloc_cb)(uv_stream_t* tcp, size_t suggested_size); -typedef void (*uv_read_cb)(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf); -typedef void (*uv_write_cb)(uv_req_t* req, int status); -typedef void (*uv_connect_cb)(uv_req_t* req, int status); -typedef void (*uv_shutdown_cb)(uv_req_t* req, int status); -typedef void (*uv_connection_cb)(uv_handle_t* server, int status); +typedef uv_buf_t (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size); +typedef void (*uv_read_cb)(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); +typedef void (*uv_write_cb)(uv_write_t* req, int status); +typedef void (*uv_connect_cb)(uv_connect_t* req, int status); +typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status); +typedef void (*uv_connection_cb)(uv_stream_t* server, int status); typedef void (*uv_close_cb)(uv_handle_t* handle); typedef void (*uv_timer_cb)(uv_timer_t* handle, int status); /* TODO: do these really need a status argument? */ @@ -80,7 +132,21 @@ typedef void (*uv_async_cb)(uv_async_t* handle, int status); typedef void (*uv_prepare_cb)(uv_prepare_t* handle, int status); typedef void (*uv_check_cb)(uv_check_t* handle, int status); typedef void (*uv_idle_cb)(uv_idle_t* handle, int status); -typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status, struct addrinfo* res); +typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status, + struct addrinfo* res); +typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal); +typedef void (*uv_fs_cb)(uv_fs_t* req); +typedef void (*uv_work_cb)(uv_work_t* req); +typedef void (*uv_after_work_cb)(uv_work_t* req); + +/* +* This will be called repeatedly after the uv_fs_event_t is initialized. +* If uv_fs_event_t was initialized with a directory the filename parameter +* will be a relative path to a file contained in the directory. +* The events paramenter is an ORed mask of enum uv_fs_event elements. +*/ +typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, + int events, int status); /* Expand this list if necessary. */ @@ -106,6 +172,7 @@ typedef enum { UV_EINVAL, UV_EISCONN, UV_EMFILE, + UV_EMSGSIZE, UV_ENETDOWN, UV_ENETUNREACH, UV_ENFILE, @@ -116,6 +183,8 @@ typedef enum { UV_ENOTCONN, UV_ENOTSOCK, UV_ENOTSUP, + UV_ENOENT, + UV_EPIPE, UV_EPROTO, UV_EPROTONOSUPPORT, UV_EPROTOTYPE, @@ -124,12 +193,15 @@ typedef enum { UV_EAIFAMNOSUPPORT, UV_EAINONAME, UV_EAISERVICE, - UV_EAISOCKTYPE + UV_EAISOCKTYPE, + UV_ESHUTDOWN, + UV_EEXIST } uv_err_code; typedef enum { UV_UNKNOWN_HANDLE = 0, UV_TCP, + UV_UDP, UV_NAMED_PIPE, UV_TTY, UV_FILE, @@ -138,9 +210,10 @@ typedef enum { UV_CHECK, UV_IDLE, UV_ASYNC, - UV_ARES, UV_ARES_TASK, - UV_GETADDRINFO + UV_ARES_EVENT, + UV_PROCESS, + UV_FS_EVENT } uv_handle_type; typedef enum { @@ -150,7 +223,12 @@ typedef enum { UV_READ, UV_WRITE, UV_SHUTDOWN, - UV_WAKEUP + UV_WAKEUP, + UV_UDP_SEND, + UV_FS, + UV_WORK, + UV_GETADDRINFO, + UV_REQ_TYPE_PRIVATE } uv_req_type; @@ -162,27 +240,55 @@ struct uv_err_s { }; -struct uv_req_s { - /* read-only */ - uv_req_type type; - /* public */ - uv_handle_t* handle; - void *(*cb)(void *); - void* data; - /* private */ +/* + * Most functions return boolean: 0 for success and -1 for failure. + * On error the user should then call uv_last_error() to determine + * the error code. + */ +uv_err_t uv_last_error(uv_loop_t*); +char* uv_strerror(uv_err_t err); +const char* uv_err_name(uv_err_t err); + + +#define UV_REQ_FIELDS \ + /* read-only */ \ + uv_req_type type; \ + /* public */ \ + void* data; \ + /* private */ \ UV_REQ_PRIVATE_FIELDS + +/* Abstract base class of all requests. */ +struct uv_req_s { + UV_REQ_FIELDS }; -/* - * Initialize a request for use with uv_write, uv_shutdown, or uv_connect. - */ -void uv_req_init(uv_req_t* req, uv_handle_t* handle, void *(*cb)(void *)); -int uv_shutdown(uv_req_t* req); +/* Platform-specific request types */ +UV_PRIVATE_REQ_TYPES + + +/* + * uv_shutdown_t is a subclass of uv_req_t + * + * Shutdown the outgoing (write) side of a duplex stream. It waits for + * pending write requests to complete. The handle should refer to a + * initialized stream. req should be an uninitalized shutdown request + * struct. The cb is a called after shutdown is complete. + */ +int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb); + +struct uv_shutdown_s { + UV_REQ_FIELDS + uv_stream_t* handle; + uv_shutdown_cb cb; + UV_SHUTDOWN_PRIVATE_FIELDS +}; #define UV_HANDLE_FIELDS \ /* read-only */ \ + uv_loop_t* loop; \ uv_handle_type type; \ /* public */ \ uv_close_cb close_cb; \ @@ -204,8 +310,21 @@ int uv_is_active(uv_handle_t* handle); /* * Request handle to be closed. close_cb will be called asynchronously after * this call. This MUST be called on each handle before memory is released. + * + * Note that handles that wrap file descriptors are closed immediately but + * close_cb will still be deferred to the next iteration of the event loop. + * It gives you a chance to free up any resources associated with the handle. */ -int uv_close(uv_handle_t* handle, uv_close_cb close_cb); +void uv_close(uv_handle_t* handle, uv_close_cb close_cb); + + +/* + * Constructor for uv_buf_t. + * Due to platform differences the user cannot rely on the ordering of the + * base and len members of the uv_buf_t struct. The user is responsible for + * freeing base after the uv_buf_t is done. Return struct passed by value. + */ +uv_buf_t uv_buf_init(char* base, size_t len); #define UV_STREAM_FIELDS \ @@ -214,13 +333,23 @@ int uv_close(uv_handle_t* handle, uv_close_cb close_cb); /* private */ \ UV_STREAM_PRIVATE_FIELDS -/* The abstract base class for all streams. */ +/* + * uv_stream_t is a subclass of uv_handle_t + * + * uv_stream is an abstract class. + * + * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t, uv_tty_t + * and soon uv_file_t. + */ struct uv_stream_s { UV_HANDLE_FIELDS UV_STREAM_FIELDS }; -/* This call is used in conjunction with uv_listen() to accept incoming +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); + +/* + * This call is used in conjunction with uv_listen() to accept incoming * connections. Call uv_accept after receiving a uv_connection_cb to accept * the connection. Before calling uv_accept use uv_*_init() must be * called on the client. Non-zero return value indicates an error. @@ -230,9 +359,10 @@ struct uv_stream_s { * once, it may fail. It is suggested to only call uv_accept once per * uv_connection_cb call. */ -int uv_accept(uv_handle_t* server, uv_stream_t* client); +int uv_accept(uv_stream_t* server, uv_stream_t* client); -/* Read data from an incoming stream. The callback will be made several +/* + * Read data from an incoming stream. The callback will be made several * several times until there is no more data to read or uv_read_stop is * called. When we've reached EOF nread will be set to -1 and the error is * set to UV_EOF. When nread == -1 the buf parameter might not point to a @@ -245,7 +375,16 @@ int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb); int uv_read_stop(uv_stream_t*); -/* Write data to stream. Buffers are written in order. Example: +typedef enum { + UV_STDIN = 0, + UV_STDOUT, + UV_STDERR +} uv_std_type; + +uv_stream_t* uv_std_handle(uv_loop_t*, uv_std_type type); + +/* + * Write data to stream. Buffers are written in order. Example: * * uv_buf_t a[] = { * { .base = "1", .len = 1 }, @@ -258,17 +397,27 @@ int uv_read_stop(uv_stream_t*); * }; * * // writes "1234" - * uv_write(req, a, 2); - * uv_write(req, b, 2); + * uv_write(req, stream, a, 2); + * uv_write(req, stream, b, 2); * */ -int uv_write(uv_req_t* req, uv_buf_t bufs[], int bufcnt); +int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb); + +/* uv_write_t is a subclass of uv_req_t */ +struct uv_write_s { + UV_REQ_FIELDS + uv_write_cb cb; + uv_stream_t* handle; + UV_WRITE_PRIVATE_FIELDS +}; + /* - * A subclass of uv_stream_t representing a TCP stream or TCP server. In the - * future this will probably be split into two classes - one a stream and - * the other a server. + * uv_tcp_t is a subclass of uv_stream_t + * + * Represents a TCP stream or TCP server. */ struct uv_tcp_s { UV_HANDLE_FIELDS @@ -276,28 +425,251 @@ struct uv_tcp_s { UV_TCP_PRIVATE_FIELDS }; -int uv_tcp_init(uv_tcp_t* handle); +int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle); int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in); int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6); +int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, int* namelen); +int uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, int* namelen); -int uv_tcp_connect(uv_req_t* req, struct sockaddr_in); -int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6); +/* + * uv_tcp_connect, uv_tcp_connect6 + * These functions establish IPv4 and IPv6 TCP connections. Provide an + * initialized TCP handle and an uninitialized uv_connect_t*. The callback + * will be made when the connection is estabished. + */ +int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in address, uv_connect_cb cb); +int uv_tcp_connect6(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in6 address, uv_connect_cb cb); -int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); +/* uv_connect_t is a subclass of uv_req_t */ +struct uv_connect_s { + UV_REQ_FIELDS + uv_connect_cb cb; + uv_stream_t* handle; + UV_CONNECT_PRIVATE_FIELDS +}; /* - * Subclass of uv_handle_t. libev wrapper. Every active prepare handle gets - * its callback called exactly once per loop iteration, just before the - * system blocks to wait for completed i/o. + * UDP support. + */ + +enum uv_udp_flags { + /* Disables dual stack mode. Used with uv_udp_bind6(). */ + UV_UDP_IPV6ONLY = 1, + /* + * Indicates message was truncated because read buffer was too small. The + * remainder was discarded by the OS. Used in uv_udp_recv_cb. + */ + UV_UDP_PARTIAL = 2 +}; + +/* + * Called after a uv_udp_send() or uv_udp_send6(). status 0 indicates + * success otherwise error. + */ +typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); + +/* + * Callback that is invoked when a new UDP datagram is received. + * + * handle UDP handle. + * nread Number of bytes that have been received. + * 0 if there is no more data to read. You may + * discard or repurpose the read buffer. + * -1 if a transmission error was detected. + * buf uv_buf_t with the received data. + * addr struct sockaddr_in or struct sockaddr_in6. + * Valid for the duration of the callback only. + * flags One or more OR'ed UV_UDP_* constants. + * Right now only UV_UDP_PARTIAL is used. + */ +typedef void (*uv_udp_recv_cb)(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, + struct sockaddr* addr, unsigned flags); + +/* uv_udp_t is a subclass of uv_handle_t */ +struct uv_udp_s { + UV_HANDLE_FIELDS + UV_UDP_PRIVATE_FIELDS +}; + +/* uv_udp_send_t is a subclass of uv_req_t */ +struct uv_udp_send_s { + UV_REQ_FIELDS + uv_udp_t* handle; + uv_udp_send_cb cb; + UV_UDP_SEND_PRIVATE_FIELDS +}; + +/* + * Initialize a new UDP handle. The actual socket is created lazily. + * Returns 0 on success. + */ +int uv_udp_init(uv_loop_t*, uv_udp_t* handle); + +/* + * Bind to a IPv4 address and port. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * addr struct sockaddr_in with the address and port to bind to. + * flags Unused. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_bind(uv_udp_t* handle, struct sockaddr_in addr, unsigned flags); + +/* + * Bind to a IPv6 address and port. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * addr struct sockaddr_in with the address and port to bind to. + * flags Should be 0 or UV_UDP_IPV6ONLY. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags); +int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, int* namelen); + +/* + * Send data. If the socket has not previously been bound with `uv_udp_bind` + * or `uv_udp_bind6`, it is bound to 0.0.0.0 (the "all interfaces" address) + * and a random port number. + * + * Arguments: + * req UDP request handle. Need not be initialized. + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * bufs List of buffers to send. + * bufcnt Number of buffers in `bufs`. + * addr Address of the remote peer. See `uv_ip4_addr`. + * send_cb Callback to invoke when the data has been sent out. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in addr, uv_udp_send_cb send_cb); + +/* + * Send data. If the socket has not previously been bound with `uv_udp_bind6`, + * it is bound to ::0 (the "all interfaces" address) and a random port number. + * + * Arguments: + * req UDP request handle. Need not be initialized. + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * bufs List of buffers to send. + * bufcnt Number of buffers in `bufs`. + * addr Address of the remote peer. See `uv_ip6_addr`. + * send_cb Callback to invoke when the data has been sent out. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_send6(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in6 addr, uv_udp_send_cb send_cb); + +/* + * Send data. If the socket has not previously been bound with `uv_udp_bind` + * or `uv_udp_bind6`, it is bound to 0.0.0.0 (the "all interfaces" address) + * and a random port number. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * alloc_cb Callback to invoke when temporary storage is needed. + * recv_cb Callback to invoke with received data. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, + uv_udp_recv_cb recv_cb); + +/* + * Stop listening for incoming datagrams. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_recv_stop(uv_udp_t* handle); + + +/* + * uv_tty_t is a subclass of uv_stream_t + * + * Representing a stream for the console. + */ +struct uv_tty_s { + UV_HANDLE_FIELDS + UV_STREAM_FIELDS + UV_TTY_PRIVATE_FIELDS +}; + +int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd); + +/* + * Set mode. 0 for normal, 1 for raw. + */ +int uv_tty_set_mode(uv_tty_t*, int mode); + +/* + * Gets the current Window size. On success zero is returned. + */ +int uv_tty_get_winsize(uv_tty_t*, int* width, int* height); + +/* + * Used to detect what type of stream should be used with a given file + * descriptor. Usually this will be used during initialization to guess the + * type of the stdio streams. + * For isatty() functionality use this function and test for UV_TTY. + */ +uv_handle_type uv_guess_handle(uv_file file); + +/* + * uv_pipe_t is a subclass of uv_stream_t + * + * Representing a pipe stream or pipe server. On Windows this is a Named + * Pipe. On Unix this is a UNIX domain socket. + */ +struct uv_pipe_s { + UV_HANDLE_FIELDS + UV_STREAM_FIELDS + UV_PIPE_PRIVATE_FIELDS +}; + +int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle); + +/* + * Opens an existing file descriptor or HANDLE as a pipe. + */ +void uv_pipe_open(uv_pipe_t*, uv_file file); + +int uv_pipe_bind(uv_pipe_t* handle, const char* name); + +int uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, + const char* name, uv_connect_cb cb); + + +/* + * uv_prepare_t is a subclass of uv_handle_t. + * + * libev wrapper. Every active prepare handle gets its callback called + * exactly once per loop iteration, just before the system blocks to wait + * for completed i/o. */ struct uv_prepare_s { UV_HANDLE_FIELDS UV_PREPARE_PRIVATE_FIELDS }; -int uv_prepare_init(uv_prepare_t* prepare); +int uv_prepare_init(uv_loop_t*, uv_prepare_t* prepare); int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb); @@ -305,16 +677,17 @@ int uv_prepare_stop(uv_prepare_t* prepare); /* - * Subclass of uv_handle_t. libev wrapper. Every active check handle gets - * its callback called exactly once per loop iteration, just after the - * system returns from blocking. + * uv_check_t is a subclass of uv_handle_t. + * + * libev wrapper. Every active check handle gets its callback called exactly + * once per loop iteration, just after the system returns from blocking. */ struct uv_check_s { UV_HANDLE_FIELDS UV_CHECK_PRIVATE_FIELDS }; -int uv_check_init(uv_check_t* check); +int uv_check_init(uv_loop_t*, uv_check_t* check); int uv_check_start(uv_check_t* check, uv_check_cb cb); @@ -322,17 +695,19 @@ int uv_check_stop(uv_check_t* check); /* - * Subclass of uv_handle_t. libev wrapper. Every active idle handle gets its - * callback called repeatedly until it is stopped. This happens after all - * other types of callbacks are processed. When there are multiple "idle" - * handles active, their callbacks are called in turn. + * uv_idle_t is a subclass of uv_handle_t. + * + * libev wrapper. Every active idle handle gets its callback called + * repeatedly until it is stopped. This happens after all other types of + * callbacks are processed. When there are multiple "idle" handles active, + * their callbacks are called in turn. */ struct uv_idle_s { UV_HANDLE_FIELDS UV_IDLE_PRIVATE_FIELDS }; -int uv_idle_init(uv_idle_t* idle); +int uv_idle_init(uv_loop_t*, uv_idle_t* idle); int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb); @@ -340,7 +715,9 @@ int uv_idle_stop(uv_idle_t* idle); /* - * Subclass of uv_handle_t. libev wrapper. uv_async_send wakes up the event + * uv_async_t is a subclass of uv_handle_t. + * + * libev wrapper. uv_async_send wakes up the event * loop and calls the async handle's callback There is no guarantee that * every uv_async_send call leads to exactly one invocation of the callback; * The only guarantee is that the callback function is called at least once @@ -352,23 +729,31 @@ struct uv_async_s { UV_ASYNC_PRIVATE_FIELDS }; -int uv_async_init(uv_async_t* async, uv_async_cb async_cb); +int uv_async_init(uv_loop_t*, uv_async_t* async, uv_async_cb async_cb); +/* + * This can be called from other threads to wake up a libuv thread. + * + * libuv is single threaded at the moment. + */ int uv_async_send(uv_async_t* async); /* - * Subclass of uv_handle_t. Wraps libev's ev_timer watcher. Used to get - * woken up at a specified time in the future. + * uv_timer_t is a subclass of uv_handle_t. + * + * Wraps libev's ev_timer watcher. Used to get woken up at a specified time + * in the future. */ struct uv_timer_s { UV_HANDLE_FIELDS UV_TIMER_PRIVATE_FIELDS }; -int uv_timer_init(uv_timer_t* timer); +int uv_timer_init(uv_loop_t*, uv_timer_t* timer); -int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, int64_t repeat); +int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, + int64_t repeat); int uv_timer_stop(uv_timer_t* timer); @@ -391,63 +776,286 @@ int64_t uv_timer_get_repeat(uv_timer_t* timer); /* c-ares integration initialize and terminate */ -int uv_ares_init_options(ares_channel *channelptr, - struct ares_options *options, - int optmask); +int uv_ares_init_options(uv_loop_t*, + ares_channel *channelptr, + struct ares_options *options, + int optmask); -void uv_ares_destroy(ares_channel channel); +/* TODO remove the loop argument from this function? */ +void uv_ares_destroy(uv_loop_t*, ares_channel channel); /* - * Subclass of uv_handle_t. Used for integration of getaddrinfo. + * uv_getaddrinfo_t is a subclass of uv_req_t + * + * Request object for uv_getaddrinfo. */ struct uv_getaddrinfo_s { - UV_HANDLE_FIELDS + UV_REQ_FIELDS + /* read-only */ + uv_loop_t* loop; \ UV_GETADDRINFO_PRIVATE_FIELDS }; -/* uv_getaddrinfo - * return code of UV_OK means that request is accepted, - * and callback will be called with result. - * Other return codes mean that there will not be a callback. +/* + * Asynchronous getaddrinfo(3). + * + * Return code 0 means that request is accepted and callback will be called + * with result. Other return codes mean that there will not be a callback. * Input arguments may be released after return from this call. - * Callback must not call freeaddrinfo + * + * uv_freeaddrinfo() must be called after completion to free the addrinfo + * structure. */ - int uv_getaddrinfo(uv_getaddrinfo_t* handle, + int uv_getaddrinfo(uv_loop_t*, + uv_getaddrinfo_t* handle, uv_getaddrinfo_cb getaddrinfo_cb, const char* node, const char* service, const struct addrinfo* hints); +void uv_freeaddrinfo(struct addrinfo* ai); + +/* uv_spawn() options */ +typedef struct uv_process_options_s { + uv_exit_cb exit_cb; /* Called after the process exits. */ + const char* file; /* Path to program to execute. */ + /* + * Command line arguments. args[0] should be the path to the program. On + * Windows this uses CreateProcess which concatinates the arguments into a + * string this can cause some strange errors. See the note at + * windows_verbatim_arguments. + */ + char** args; + /* + * This will be set as the environ variable in the subprocess. If this is + * NULL then the parents environ will be used. + */ + char** env; + /* + * If non-null this represents a directory the subprocess should execute + * in. Stands for current working directory. + */ + char* cwd; + + /* + * TODO describe how this works. + */ + int windows_verbatim_arguments; + + /* + * The user should supply pointers to initialized uv_pipe_t structs for + * stdio. This is used to to send or receive input from the subprocess. + * The user is reponsible for calling uv_close on them. + */ + uv_pipe_t* stdin_stream; + uv_pipe_t* stdout_stream; + uv_pipe_t* stderr_stream; +} uv_process_options_t; /* - * Most functions return boolean: 0 for success and -1 for failure. - * On error the user should then call uv_last_error() to determine - * the error code. + * uv_process_t is a subclass of uv_handle_t */ -uv_err_t uv_last_error(); -char* uv_strerror(uv_err_t err); -const char* uv_err_name(uv_err_t err); +struct uv_process_s { + UV_HANDLE_FIELDS + uv_exit_cb exit_cb; + int pid; + UV_PROCESS_PRIVATE_FIELDS +}; -void uv_init(); -int uv_run(); +/* Initializes uv_process_t and starts the process. */ +int uv_spawn(uv_loop_t*, uv_process_t*, uv_process_options_t options); /* - * Manually modify the event loop's reference count. Useful if the user wants - * to have a handle or timeout that doesn't keep the loop alive. + * Kills the process with the specified signal. The user must still + * call uv_close on the process. */ -void uv_ref(); -void uv_unref(); +int uv_process_kill(uv_process_t*, int signum); -void uv_update_time(); -int64_t uv_now(); +/* + * uv_work_t is a subclass of uv_req_t + */ +struct uv_work_s { + UV_REQ_FIELDS + uv_loop_t* loop; + uv_work_cb work_cb; + uv_after_work_cb after_work_cb; + UV_WORK_PRIVATE_FIELDS +}; + +/* Queues a work request to execute asynchronously on the thread pool. */ +int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, + uv_after_work_cb after_work_cb); + + + + +/* + * File System Methods. + * + * The uv_fs_* functions execute a blocking system call asynchronously (in a + * thread pool) and call the specified callback in the specified loop after + * completion. If the user gives NULL as the callback the blocking system + * call will be called synchronously. req should be a pointer to an + * uninitialized uv_fs_t object. + * + * uv_fs_req_cleanup() must be called after completion of the uv_fs_ + * function to free any internal memory allocations associted with the + * request. + */ + +typedef enum { + UV_FS_UNKNOWN = -1, + UV_FS_CUSTOM, + UV_FS_OPEN, + UV_FS_CLOSE, + UV_FS_READ, + UV_FS_WRITE, + UV_FS_SENDFILE, + UV_FS_STAT, + UV_FS_LSTAT, + UV_FS_FSTAT, + UV_FS_FTRUNCATE, + UV_FS_UTIME, + UV_FS_FUTIME, + UV_FS_CHMOD, + UV_FS_FCHMOD, + UV_FS_FSYNC, + UV_FS_FDATASYNC, + UV_FS_UNLINK, + UV_FS_RMDIR, + UV_FS_MKDIR, + UV_FS_RENAME, + UV_FS_READDIR, + UV_FS_LINK, + UV_FS_SYMLINK, + UV_FS_READLINK, + UV_FS_CHOWN, + UV_FS_FCHOWN +} uv_fs_type; + +/* uv_fs_t is a subclass of uv_req_t */ +struct uv_fs_s { + UV_REQ_FIELDS + uv_loop_t* loop; + uv_fs_type fs_type; + uv_fs_cb cb; + ssize_t result; + void* ptr; + char* path; + int errorno; + UV_FS_PRIVATE_FIELDS +}; + +void uv_fs_req_cleanup(uv_fs_t* req); + +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + int mode, uv_fs_cb cb); + +int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb); + +int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb); + +int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb); + +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + uv_fs_cb cb); + +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb); + +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, + off_t offset, uv_fs_cb cb); + +int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, + uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb); + +int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb); + +int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb); + +int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, + double mtime, uv_fs_cb cb); + +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb); + +/* + * This flag can be used with uv_fs_symlink on Windows + * to specify whether path argument points to a directory. + */ +#define UV_FS_SYMLINK_DIR 0x0001 + +int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, int flags, uv_fs_cb cb); + +int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb); + +int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, + uv_fs_cb cb); + +int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid, + int gid, uv_fs_cb cb); + +int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, + int gid, uv_fs_cb cb); + + +enum uv_fs_event { + UV_RENAME = 1, + UV_CHANGE = 2 +}; + + +struct uv_fs_event_s { + UV_HANDLE_FIELDS + char* filename; + UV_FS_EVENT_PRIVATE_FIELDS +}; + + +/* +* If filename is a directory then we will watch for all events in that +* directory. If filename is a file - we will only get events from that +* file. Subdirectories are not watched. +*/ +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb); /* Utility */ + +/* Convert string ip addresses to binary structures */ struct sockaddr_in uv_ip4_addr(const char* ip, int port); struct sockaddr_in6 uv_ip6_addr(const char* ip, int port); +/* Convert binary addresses to strings */ +int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size); +int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size); + /* Gets the executable path */ int uv_exepath(char* buffer, size_t* size); @@ -463,33 +1071,67 @@ int uv_exepath(char* buffer, size_t* size); extern uint64_t uv_hrtime(void); -/* the presence of this union forces similar struct layout */ +/* the presence of these unions force similar struct layout */ union uv_any_handle { uv_tcp_t tcp; + uv_pipe_t pipe; uv_prepare_t prepare; uv_check_t check; uv_idle_t idle; uv_async_t async; uv_timer_t timer; uv_getaddrinfo_t getaddrinfo; + uv_fs_event_t fs_event; }; -/* Diagnostic counters */ -typedef struct { +union uv_any_req { + uv_req_t req; + uv_write_t write; + uv_connect_t connect; + uv_shutdown_t shutdown; + uv_fs_t fs_req; + uv_work_t work_req; +}; + + +struct uv_counters_s { + uint64_t eio_init; uint64_t req_init; uint64_t handle_init; + uint64_t stream_init; uint64_t tcp_init; + uint64_t udp_init; + uint64_t pipe_init; + uint64_t tty_init; uint64_t prepare_init; uint64_t check_init; uint64_t idle_init; uint64_t async_init; uint64_t timer_init; -} uv_counters_t; + uint64_t process_init; + uint64_t fs_event_init; +}; -uv_counters_t* uv_counters(); + +struct uv_loop_s { + UV_LOOP_PRIVATE_FIELDS + /* list used for ares task handles */ + uv_ares_task_t* uv_ares_handles_; + /* Various thing for libeio. */ + uv_async_t uv_eio_want_poll_notifier; + uv_async_t uv_eio_done_poll_notifier; + uv_idle_t uv_eio_poller; + /* Diagnostic counters */ + uv_counters_t counters; + /* The last error */ + uv_err_t last_err; + /* User data - use this for whatever. */ + void* data; +}; /* Don't export the private CPP symbols. */ +#undef UV_REQ_TYPE_PRIVATE #undef UV_REQ_PRIVATE_FIELDS #undef UV_STREAM_PRIVATE_FIELDS #undef UV_TCP_PRIVATE_FIELDS @@ -499,6 +1141,9 @@ uv_counters_t* uv_counters(); #undef UV_ASYNC_PRIVATE_FIELDS #undef UV_TIMER_PRIVATE_FIELDS #undef UV_GETADDRINFO_PRIVATE_FIELDS +#undef UV_FS_REQ_PRIVATE_FIELDS +#undef UV_WORK_PRIVATE_FIELDS +#undef UV_FS_EVENT_PRIVATE_FIELDS #ifdef __cplusplus } diff --git a/src/rt/libuv/msvs/c-ares.vcxproj b/src/rt/libuv/msvs/c-ares.vcxproj deleted file mode 100644 index 4ada6ccb3df..00000000000 --- a/src/rt/libuv/msvs/c-ares.vcxproj +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - - - - StaticLibrary - true - - - StaticLibrary - true - - - StaticLibrary - false - - - StaticLibrary - false - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;HAVE_CONFIG_H;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - WIN32;HAVE_CONFIG_H;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - WIN32;HAVE_CONFIG_H;_LIB;%(PreprocessorDefinitions) - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - WIN32;HAVE_CONFIG_H;_LIB;%(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/rt/libuv/msvs/libuv-benchmark.vcxproj b/src/rt/libuv/msvs/libuv-benchmark.vcxproj deleted file mode 100644 index 5727a1aa3c8..00000000000 --- a/src/rt/libuv/msvs/libuv-benchmark.vcxproj +++ /dev/null @@ -1,167 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - - - - Application - true - - - Application - true - - - Application - false - - - Application - false - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - {301fe650-cd34-14e5-6b63-42e383fa02bc} - - - - - - diff --git a/src/rt/libuv/msvs/libuv-test.vcxproj b/src/rt/libuv/msvs/libuv-test.vcxproj deleted file mode 100644 index 5b47daeb201..00000000000 --- a/src/rt/libuv/msvs/libuv-test.vcxproj +++ /dev/null @@ -1,180 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744} - - - - Application - true - - - Application - true - - - Application - false - - - Application - false - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {301fe650-cd34-14e5-6b63-42e383fa02bc} - - - - - - diff --git a/src/rt/libuv/msvs/libuv.sln b/src/rt/libuv/msvs/libuv.sln deleted file mode 100644 index a4eca0a6d4b..00000000000 --- a/src/rt/libuv/msvs/libuv.sln +++ /dev/null @@ -1,56 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuv", "libuv.vcxproj", "{301FE650-CD34-14E5-6B63-42E383FA02BC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuv-test", "libuv-test.vcxproj", "{1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuv-benchmark", "libuv-benchmark.vcxproj", "{6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "c-ares", "c-ares.vcxproj", "{2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|Win32.ActiveCfg = Debug|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|Win32.Build.0 = Debug|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|x64.ActiveCfg = Debug|x64 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|x64.Build.0 = Debug|x64 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|Win32.ActiveCfg = Release|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|Win32.Build.0 = Release|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|x64.ActiveCfg = Release|x64 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|x64.Build.0 = Release|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|Win32.ActiveCfg = Debug|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|Win32.Build.0 = Debug|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|x64.ActiveCfg = Debug|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|x64.Build.0 = Debug|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|Win32.ActiveCfg = Release|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|Win32.Build.0 = Release|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|x64.ActiveCfg = Release|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|x64.Build.0 = Release|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|Win32.ActiveCfg = Debug|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|Win32.Build.0 = Debug|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|x64.ActiveCfg = Debug|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|x64.Build.0 = Debug|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|Win32.ActiveCfg = Release|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|Win32.Build.0 = Release|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|x64.ActiveCfg = Release|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|x64.Build.0 = Release|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|Win32.ActiveCfg = Debug|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|Win32.Build.0 = Debug|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|x64.ActiveCfg = Debug|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|x64.Build.0 = Debug|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|Win32.ActiveCfg = Release|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|Win32.Build.0 = Release|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|x64.ActiveCfg = Release|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/rt/libuv/msvs/libuv.vcxproj b/src/rt/libuv/msvs/libuv.vcxproj deleted file mode 100644 index c896c6970f0..00000000000 --- a/src/rt/libuv/msvs/libuv.vcxproj +++ /dev/null @@ -1,131 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - {301FE650-CD34-14E5-6B63-42E383FA02BC} - - - - StaticLibrary - true - - - StaticLibrary - true - - - StaticLibrary - false - - - StaticLibrary - false - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - - - - - {2b6a4644-eba9-dfb5-af35-6c56edf05c7f} - true - true - false - true - false - - - - - - - - - - - - - - - - - - diff --git a/src/rt/libuv/src/ares/ares_parse_a_reply.c b/src/rt/libuv/src/ares/ares_parse_a_reply.c index 93012549c90..9926c27c577 100644 --- a/src/rt/libuv/src/ares/ares_parse_a_reply.c +++ b/src/rt/libuv/src/ares/ares_parse_a_reply.c @@ -238,6 +238,8 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, for (i = 0; i < naddrs; i++) hostent->h_addr_list[i] = (char *) &addrs[i]; hostent->h_addr_list[naddrs] = NULL; + if (!naddrs && addrs) + free(addrs); *host = hostent; return ARES_SUCCESS; } diff --git a/src/rt/libuv/src/ares/config_netbsd/ares_config.h b/src/rt/libuv/src/ares/config_netbsd/ares_config.h new file mode 100644 index 00000000000..5e3ae688645 --- /dev/null +++ b/src/rt/libuv/src/ares/config_netbsd/ares_config.h @@ -0,0 +1,510 @@ +/* ares_config.h. Generated from ares_config.h.in by configure. */ +/* ares_config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* define this if ares is built for a big endian system */ +/* #undef ARES_BIG_ENDIAN */ + +/* when building as static part of libcurl */ +/* #undef BUILDING_LIBCURL */ + +/* when building c-ares library */ +/* #undef CARES_BUILDING_LIBRARY */ + +/* when not building a shared library */ +/* #undef CARES_STATICLIB */ + +/* Define to 1 to enable hiding of library internal symbols. */ +/* #undef CARES_SYMBOL_HIDING */ + +/* Definition to make a library symbol externally visible. */ +/* #undef CARES_SYMBOL_SCOPE_EXTERN */ + +/* if a /etc/inet dir is being used */ +/* #undef ETC_INET */ + +/* Define to the type qualifier of arg 1 for getnameinfo. */ +#define GETNAMEINFO_QUAL_ARG1 const + +/* Define to the type of arg 1 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG1 struct sockaddr * + +/* Define to the type of arg 2 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG2 socklen_t + +/* Define to the type of args 4 and 6 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG46 size_t + +/* Define to the type of arg 7 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG7 int + +/* Specifies the number of arguments to getservbyport_r */ +#define GETSERVBYPORT_R_ARGS 4 + +/* Specifies the size of the buffer to pass to getservbyport_r */ +#define GETSERVBYPORT_R_BUFSIZE sizeof(struct servent_data) + +/* Define to 1 if you have AF_INET6. */ +#define HAVE_AF_INET6 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_NAMESER_COMPAT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_NAMESER_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the `bitncmp' function. */ +/* #undef HAVE_BITNCMP */ + +/* Define to 1 if bool is an available type. */ +#define HAVE_BOOL_T 1 + +/* Define to 1 if you have the clock_gettime function and monotonic timer. */ +#define HAVE_CLOCK_GETTIME_MONOTONIC 1 + +/* Define to 1 if you have the closesocket function. */ +/* #undef HAVE_CLOSESOCKET */ + +/* Define to 1 if you have the CloseSocket camel case function. */ +/* #undef HAVE_CLOSESOCKET_CAMEL */ + +/* Define to 1 if you have the connect function. */ +#define HAVE_CONNECT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the fcntl function. */ +#define HAVE_FCNTL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have a working fcntl O_NONBLOCK function. */ +#define HAVE_FCNTL_O_NONBLOCK 1 + +/* Define to 1 if you have the freeaddrinfo function. */ +#define HAVE_FREEADDRINFO 1 + +/* Define to 1 if you have a working getaddrinfo function. */ +#define HAVE_GETADDRINFO 1 + +/* Define to 1 if the getaddrinfo function is threadsafe. */ +/* #undef HAVE_GETADDRINFO_THREADSAFE */ + +/* Define to 1 if you have the gethostbyaddr function. */ +#define HAVE_GETHOSTBYADDR 1 + +/* Define to 1 if you have the gethostbyname function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have the getnameinfo function. */ +#define HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the getservbyport_r function. */ +#define HAVE_GETSERVBYPORT_R 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `if_indextoname' function. */ +#define HAVE_IF_INDEXTONAME 1 + +/* Define to 1 if you have the `inet_net_pton' function. */ +#define HAVE_INET_NET_PTON 1 + +/* Define to 1 if inet_net_pton supports IPv6. */ +/* #undef HAVE_INET_NET_PTON_IPV6 */ + +/* Define to 1 if you have a IPv6 capable working inet_ntop function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have a IPv6 capable working inet_pton function. */ +#define HAVE_INET_PTON 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the ioctl function. */ +#define HAVE_IOCTL 1 + +/* Define to 1 if you have the ioctlsocket function. */ +/* #undef HAVE_IOCTLSOCKET */ + +/* Define to 1 if you have the IoctlSocket camel case function. */ +/* #undef HAVE_IOCTLSOCKET_CAMEL */ + +/* Define to 1 if you have a working IoctlSocket camel case FIONBIO function. + */ +/* #undef HAVE_IOCTLSOCKET_CAMEL_FIONBIO */ + +/* Define to 1 if you have a working ioctlsocket FIONBIO function. */ +/* #undef HAVE_IOCTLSOCKET_FIONBIO */ + +/* Define to 1 if you have a working ioctl FIONBIO function. */ +#define HAVE_IOCTL_FIONBIO 1 + +/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */ +#define HAVE_IOCTL_SIOCGIFADDR 1 + +/* Define to 1 if you have the `resolve' library (-lresolve). */ +/* #undef HAVE_LIBRESOLVE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* if your compiler supports LL */ +#define HAVE_LL 1 + +/* Define to 1 if the compiler supports the 'long long' data type. */ +#define HAVE_LONGLONG 1 + +/* Define to 1 if you have the malloc.h header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the memory.h header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the MSG_NOSIGNAL flag. */ +/* #undef HAVE_MSG_NOSIGNAL */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have PF_INET6. */ +#define HAVE_PF_INET6 1 + +/* Define to 1 if you have the recv function. */ +#define HAVE_RECV 1 + +/* Define to 1 if you have the recvfrom function. */ +#define HAVE_RECVFROM 1 + +/* Define to 1 if you have the send function. */ +#define HAVE_SEND 1 + +/* Define to 1 if you have the setsockopt function. */ +#define HAVE_SETSOCKOPT 1 + +/* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */ +/* #undef HAVE_SETSOCKOPT_SO_NONBLOCK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if sig_atomic_t is an available typedef. */ +#define HAVE_SIG_ATOMIC_T 1 + +/* Define to 1 if sig_atomic_t is already defined as volatile. */ +/* #undef HAVE_SIG_ATOMIC_T_VOLATILE */ + +/* Define to 1 if your struct sockaddr_in6 has sin6_scope_id. */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* Define to 1 if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the strcasecmp function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the strcmpi function. */ +/* #undef HAVE_STRCMPI */ + +/* Define to 1 if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the stricmp function. */ +/* #undef HAVE_STRICMP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the strncasecmp function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to 1 if you have the strncmpi function. */ +/* #undef HAVE_STRNCMPI */ + +/* Define to 1 if you have the strnicmp function. */ +/* #undef HAVE_STRNICMP */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STROPTS_H */ + +/* Define to 1 if you have struct addrinfo. */ +#define HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if you have struct in6_addr. */ +#define HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if you have struct sockaddr_in6. */ +#define HAVE_STRUCT_SOCKADDR_IN6 1 + +/* if struct sockaddr_storage is defined */ +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if you have the timeval struct. */ +#define HAVE_STRUCT_TIMEVAL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the windows.h header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to 1 if you have the winsock2.h header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if you have the winsock.h header file. */ +/* #undef HAVE_WINSOCK_H */ + +/* Define to 1 if you have the writev function. */ +#define HAVE_WRITEV 1 + +/* Define to 1 if you have the ws2tcpip.h header file. */ +/* #undef HAVE_WS2TCPIP_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if you are building a native Windows target. */ +/* #undef NATIVE_WINDOWS */ + +/* Define to 1 if you need the malloc.h header file even with stdlib.h */ +/* #undef NEED_MALLOC_H */ + +/* Define to 1 if you need the memory.h header file even with stdlib.h */ +/* #undef NEED_MEMORY_H */ + +/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */ +/* #undef NEED_REENTRANT */ + +/* Define to 1 if _THREAD_SAFE preprocessor symbol must be defined. */ +/* #undef NEED_THREAD_SAFE */ + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* cpu-machine-OS */ +#define OS "i386-unknown-openbsd4.7" + +/* Name of package */ +#define PACKAGE "c-ares" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "c-ares mailing list => http://cool.haxx.se/mailman/listinfo/c-ares" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "c-ares" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "c-ares 1.7.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "c-ares" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.7.1" + +/* a suitable file/device to read random data from */ +#define RANDOM_FILE "/dev/urandom" + +/* Define to the type of arg 1 for recvfrom. */ +#define RECVFROM_TYPE_ARG1 int + +/* Define to the type pointed by arg 2 for recvfrom. */ +#define RECVFROM_TYPE_ARG2 void + +/* Define to 1 if the type pointed by arg 2 for recvfrom is void. */ +#define RECVFROM_TYPE_ARG2_IS_VOID 1 + +/* Define to the type of arg 3 for recvfrom. */ +#define RECVFROM_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for recvfrom. */ +#define RECVFROM_TYPE_ARG4 int + +/* Define to the type pointed by arg 5 for recvfrom. */ +#define RECVFROM_TYPE_ARG5 struct sockaddr + +/* Define to 1 if the type pointed by arg 5 for recvfrom is void. */ +/* #undef RECVFROM_TYPE_ARG5_IS_VOID */ + +/* Define to the type pointed by arg 6 for recvfrom. */ +#define RECVFROM_TYPE_ARG6 socklen_t + +/* Define to 1 if the type pointed by arg 6 for recvfrom is void. */ +/* #undef RECVFROM_TYPE_ARG6_IS_VOID */ + +/* Define to the function return type for recvfrom. */ +#define RECVFROM_TYPE_RETV int + +/* Define to the type of arg 1 for recv. */ +#define RECV_TYPE_ARG1 int + +/* Define to the type of arg 2 for recv. */ +#define RECV_TYPE_ARG2 void * + +/* Define to the type of arg 3 for recv. */ +#define RECV_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for recv. */ +#define RECV_TYPE_ARG4 int + +/* Define to the function return type for recv. */ +#define RECV_TYPE_RETV int + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to the type qualifier of arg 2 for send. */ +#define SEND_QUAL_ARG2 const + +/* Define to the type of arg 1 for send. */ +#define SEND_TYPE_ARG1 int + +/* Define to the type of arg 2 for send. */ +#define SEND_TYPE_ARG2 void * + +/* Define to the type of arg 3 for send. */ +#define SEND_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for send. */ +#define SEND_TYPE_ARG4 int + +/* Define to the function return type for send. */ +#define SEND_TYPE_RETV int + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 4 + +/* The size of `struct in6_addr', as computed by sizeof. */ +#define SIZEOF_STRUCT_IN6_ADDR 16 + +/* The size of `struct in_addr', as computed by sizeof. */ +#define SIZEOF_STRUCT_IN_ADDR 4 + +/* The size of `time_t', as computed by sizeof. */ +#define SIZEOF_TIME_T 4 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to disable non-blocking sockets. */ +/* #undef USE_BLOCKING_SOCKETS */ + +/* Version number of package */ +#define VERSION "1.7.1" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if OS is AIX. */ +#ifndef _ALL_SOURCE +/* # undef _ALL_SOURCE */ +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Type to use in place of in_addr_t when system does not provide it. */ +/* #undef in_addr_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* the signed version of size_t */ +/* #undef ssize_t */ diff --git a/src/rt/libuv/src/ares/inet_ntop.h b/src/rt/libuv/src/ares/inet_ntop.h index c583488f75e..1b5389e893c 100644 --- a/src/rt/libuv/src/ares/inet_ntop.h +++ b/src/rt/libuv/src/ares/inet_ntop.h @@ -18,6 +18,7 @@ */ #ifdef HAVE_INET_NTOP +#include #define ares_inet_ntop(w,x,y,z) inet_ntop(w,x,y,z) #else const char *ares_inet_ntop(int af, const void *src, char *dst, size_t size); diff --git a/src/rt/libuv/src/eio/Changes b/src/rt/libuv/src/eio/Changes deleted file mode 100644 index baa94eca9ca..00000000000 --- a/src/rt/libuv/src/eio/Changes +++ /dev/null @@ -1,35 +0,0 @@ -Revision history for libeio - -TODO: maybe add mincore support? available on at least darwin, solaris, linux, freebsd -TODO: openbsd requites stdint.h for intptr_t - why posix? - -1.0 - - readdir: correctly handle malloc failures. - - readdir: new flags argument, can return inode - and possibly filetype, can sort in various ways. - - readdir: stop immediately when cancelled, do - not continue reading the directory. - - fix return value of eio_sendfile_sync. - - include sys/mman.h for msync. - - added EIO_STACKSIZE. - - added msync, mtouch support (untested). - - added sync_file_range (untested). - - fixed custom support. - - use a more robust feed-add detection method. - - "outbundled" from IO::AIO. - - eio_set_max_polltime did not properly convert time to ticks. - - tentatively support darwin in sendfile. - - fix freebsd/darwin sendfile. - - also use sendfile emulation for ENOTSUP and EOPNOTSUPP - error codes. - - add OS-independent EIO_MT_* and EIO_MS_* flag enums. - - add eio_statvfs/eio_fstatvfs. - - add eio_mlock/eio_mlockall and OS-independent MCL_* flag enums. - - no longer set errno to 0 before making syscalls, this only lures - people into the trap of believing errno shows success or failure. - - "fix" demo.c so that it works as non-root. - - suppoert utimes seperately from futimes, as some systems have - utimes but not futimes. - - use _POSIX_MEMLOCK_RANGE for mlock. - - do not (errornously) overwrite CFLAGS in configure.ac. - diff --git a/src/rt/libuv/src/eio/autogen.sh b/src/rt/libuv/src/eio/autogen.sh deleted file mode 100755 index bd3387c46c7..00000000000 --- a/src/rt/libuv/src/eio/autogen.sh +++ /dev/null @@ -1,5 +0,0 @@ -libtoolize -aclocal -automake --add-missing -autoconf -autoheader diff --git a/src/rt/libuv/src/eio/eio.pod b/src/rt/libuv/src/eio/eio.pod deleted file mode 100644 index c83768ebf19..00000000000 --- a/src/rt/libuv/src/eio/eio.pod +++ /dev/null @@ -1,303 +0,0 @@ -=head1 NAME - -libeio - truly asynchronous POSIX I/O - -=head1 SYNOPSIS - - #include - -=head1 DESCRIPTION - -The newest version of this document is also available as an html-formatted -web page you might find easier to navigate when reading it for the first -time: L. - -Note that this library is a by-product of the C perl -module, and many of the subtler points regarding requets lifetime -and so on are only documented in its documentation at the -moment: L. - -=head2 FEATURES - -This library provides fully asynchronous versions of most POSIX functions -dealign with I/O. Unlike most asynchronous libraries, this not only -includes C and C, but also C, C, C and -similar functions, as well as less rarely ones such as C, C -or C. - -It also offers wrappers around C (Solaris, Linux, HP-UX and -FreeBSD, with emulation on other platforms) and C (Linux, with -emulation elsewhere>). - -The goal is to enable you to write fully non-blocking programs. For -example, in a game server, you would not want to freeze for a few seconds -just because the server is running a backup and you happen to call -C. - -=head2 TIME REPRESENTATION - -Libeio represents time as a single floating point number, representing the -(fractional) number of seconds since the (POSIX) epoch (somewhere near -the beginning of 1970, details are complicated, don't ask). This type is -called C, but it is guarenteed to be of type C (or -better), so you can freely use C yourself. - -Unlike the name component C might indicate, it is also used for -time differences throughout libeio. - -=head2 FORK SUPPORT - -Calling C is fully supported by this module. It is implemented in these steps: - - 1. wait till all requests in "execute" state have been handled - (basically requests that are already handed over to the kernel). - 2. fork - 3. in the parent, continue business as usual, done - 4. in the child, destroy all ready and pending requests and free the - memory used by the worker threads. This gives you a fully empty - libeio queue. - -=head1 INITIALISATION/INTEGRATION - -Before you can call any eio functions you first have to initialise the -library. The library integrates into any event loop, but can also be used -without one, including in polling mode. - -You have to provide the necessary glue yourself, however. - -=over 4 - -=item int eio_init (void (*want_poll)(void), void (*done_poll)(void)) - -This function initialises the library. On success it returns C<0>, on -failure it returns C<-1> and sets C appropriately. - -It accepts two function pointers specifying callbacks as argument, both of -which can be C<0>, in which case the callback isn't called. - -=item want_poll callback - -The C callback is invoked whenever libeio wants attention (i.e. -it wants to be polled by calling C). It is "edge-triggered", -that is, it will only be called once when eio wants attention, until all -pending requests have been handled. - -This callback is called while locks are being held, so I. That includes -C. What you should do is notify some other thread, or wake up -your event loop, and then call C. - -=item done_poll callback - -This callback is invoked when libeio detects that all pending requests -have been handled. It is "edge-triggered", that is, it will only be -called once after C. To put it differently, C and -C are invoked in pairs: after C you have to call -C until either C indicates that everything has been -handled or C has been called, which signals the same. - -Note that C might return after C and C -have been called again, so watch out for races in your code. - -As with C, this callback is called while lcoks are being held, -so you I. - -=item int eio_poll () - -This function has to be called whenever there are pending requests that -need finishing. You usually call this after C has indicated -that you should do so, but you can also call this function regularly to -poll for new results. - -If any request invocation returns a non-zero value, then C -immediately returns with that value as return value. - -Otherwise, if all requests could be handled, it returns C<0>. If for some -reason not all requests have been handled, i.e. some are still pending, it -returns C<-1>. - -=back - -For libev, you would typically use an C watcher: the -C callback would invoke C to wake up the event -loop. Inside the callback set for the watcher, one would call C (followed by C again if C indicates that not -all requests have been handled yet). The race is taken care of because -libev resets/rearms the async watcher before calling your callback, -and therefore, before calling C. This might result in (some) -spurious wake-ups, but is generally harmless. - -For most other event loops, you would typically use a pipe - the event -loop should be told to wait for read readyness on the read end. In -C you would write a single byte, in C you would try -to read that byte, and in the callback for the read end, you would call -C. The race is avoided here because the event loop should invoke -your callback again and again until the byte has been read (as the pipe -read callback does not read it, only C). - -=head2 CONFIGURATION - -The functions in this section can sometimes be useful, but the default -configuration will do in most case, so you should skip this section on -first reading. - -=over 4 - -=item eio_set_max_poll_time (eio_tstamp nseconds) - -This causes C to return after it has detected that it was -running for C seconds or longer (this number can be fractional). - -This can be used to limit the amount of time spent handling eio requests, -for example, in interactive programs, you might want to limit this time to -C<0.01> seconds or so. - -Note that: - -a) libeio doesn't know how long your request callbacks take, so the time -spent in C is up to one callback invocation longer then this -interval. - -b) this is implemented by calling C after each request, -which can be costly. - -c) at least one request will be handled. - -=item eio_set_max_poll_reqs (unsigned int nreqs) - -When C is non-zero, then C will not handle more than -C requests per invocation. This is a less costly way to limit the -amount of work done by C then setting a time limit. - -If you know your callbacks are generally fast, you could use this to -encourage interactiveness in your programs by setting it to C<10>, C<100> -or even C<1000>. - -=item eio_set_min_parallel (unsigned int nthreads) - -Make sure libeio can handle at least this many requests in parallel. It -might be able handle more. - -=item eio_set_max_parallel (unsigned int nthreads) - -Set the maximum number of threads that libeio will spawn. - -=item eio_set_max_idle (unsigned int nthreads) - -Libeio uses threads internally to handle most requests, and will start and stop threads on demand. - -This call can be used to limit the number of idle threads (threads without -work to do): libeio will keep some threads idle in preperation for more -requests, but never longer than C threads. - -In addition to this, libeio will also stop threads when they are idle for -a few seconds, regardless of this setting. - -=item unsigned int eio_nthreads () - -Return the number of worker threads currently running. - -=item unsigned int eio_nreqs () - -Return the number of requests currently handled by libeio. This is the -total number of requests that have been submitted to libeio, but not yet -destroyed. - -=item unsigned int eio_nready () - -Returns the number of ready requests, i.e. requests that have been -submitted but have not yet entered the execution phase. - -=item unsigned int eio_npending () - -Returns the number of pending requests, i.e. requests that have been -executed and have results, but have not been finished yet by a call to -C). - -=back - - -=head1 ANATOMY OF AN EIO REQUEST - -#TODO - - -=head1 HIGH LEVEL REQUEST API - -#TODO - -=back - - -=head1 LOW LEVEL REQUEST API - -#TODO - -=head1 EMBEDDING - -Libeio can be embedded directly into programs. This functionality is not -documented and not (yet) officially supported. - -Note that, when including C, you are responsible for defining -the compilation environment (C<_LARGEFILE_SOURCE>, C<_GNU_SOURCE> etc.). - -If you need to know how, check the C perl module, which does -exactly that. - - -=head1 COMPILETIME CONFIGURATION - -These symbols, if used, must be defined when compiling F. - -=over 4 - -=item EIO_STACKSIZE - -This symbol governs the stack size for each eio thread. Libeio itself -was written to use very little stackspace, but when using C -requests, you might want to increase this. - -If this symbol is undefined (the default) then libeio will use its default -stack size (C currently). If it is defined, but -C<0>, then the default operating system stack size will be used. In all -other cases, the value must be an expression that evaluates to the desired -stack size. - -=back - - -=head1 PORTABILITY REQUIREMENTS - -In addition to a working ISO-C implementation, libeio relies on a few -additional extensions: - -=over 4 - -=item POSIX threads - -To be portable, this module uses threads, specifically, the POSIX threads -library must be available (and working, which partially excludes many xBSD -systems, where C is buggy). - -=item POSIX-compatible filesystem API - -This is actually a harder portability requirement: The libeio API is quite -demanding regarding POSIX API calls (symlinks, user/group management -etc.). - -=item C must hold a time value in seconds with enough accuracy - -The type C is used to represent timestamps. It is required to -have at least 51 bits of mantissa (and 9 bits of exponent), which is good -enough for at least into the year 4000. This requirement is fulfilled by -implementations implementing IEEE 754 (basically all existing ones). - -=back - -If you know of other additional requirements drop me a note. - - -=head1 AUTHOR - -Marc Lehmann . - diff --git a/src/rt/libuv/src/unix/cares.c b/src/rt/libuv/src/unix/cares.c new file mode 100644 index 00000000000..a2466f59e6f --- /dev/null +++ b/src/rt/libuv/src/unix/cares.c @@ -0,0 +1,185 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + + +/* + * This is called once per second by loop->timer. It is used to + * constantly callback into c-ares for possibly processing timeouts. + */ +static void uv__ares_timeout(struct ev_loop* ev, struct ev_timer* watcher, + int revents) { + uv_loop_t* loop = ev_userdata(ev); + + assert(ev == loop->ev); + assert((uv_loop_t*)watcher->data == loop); + assert(watcher == &loop->timer); + assert(revents == EV_TIMER); + assert(!uv_ares_handles_empty(loop)); + + ares_process_fd(loop->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); +} + + +static void uv__ares_io(struct ev_loop* ev, struct ev_io* watcher, + int revents) { + uv_loop_t* loop = ev_userdata(ev); + + assert(ev == loop->ev); + + /* Reset the idle timer */ + ev_timer_again(ev, &loop->timer); + + /* Process DNS responses */ + ares_process_fd(loop->channel, + revents & EV_READ ? watcher->fd : ARES_SOCKET_BAD, + revents & EV_WRITE ? watcher->fd : ARES_SOCKET_BAD); +} + + +/* Allocates and returns a new uv_ares_task_t */ +static uv_ares_task_t* uv__ares_task_create(int fd) { + uv_ares_task_t* h = malloc(sizeof(uv_ares_task_t)); + + if (h == NULL) { + uv_fatal_error(ENOMEM, "malloc"); + } + + h->sock = fd; + + ev_io_init(&h->read_watcher, uv__ares_io, fd, EV_READ); + ev_io_init(&h->write_watcher, uv__ares_io, fd, EV_WRITE); + + h->read_watcher.data = h; + h->write_watcher.data = h; + + return h; +} + + +/* Callback from ares when socket operation is started */ +static void uv__ares_sockstate_cb(void* data, ares_socket_t sock, + int read, int write) { + uv_loop_t* loop = data; + uv_ares_task_t* h; + + assert((uv_loop_t*)loop->timer.data == loop); + + h = uv_find_ares_handle(loop, sock); + + if (read || write) { + if (!h) { + /* New socket */ + + /* If this is the first socket then start the timer. */ + if (!ev_is_active(&loop->timer)) { + assert(uv_ares_handles_empty(loop)); + ev_timer_again(loop->ev, &loop->timer); + } + + h = uv__ares_task_create(sock); + uv_add_ares_handle(loop, h); + } + + if (read) { + ev_io_start(loop->ev, &h->read_watcher); + } else { + ev_io_stop(loop->ev, &h->read_watcher); + } + + if (write) { + ev_io_start(loop->ev, &h->write_watcher); + } else { + ev_io_stop(loop->ev, &h->write_watcher); + } + + } else { + /* + * read == 0 and write == 0 this is c-ares's way of notifying us that + * the socket is now closed. We must free the data associated with + * socket. + */ + assert(h && "When an ares socket is closed we should have a handle for it"); + + ev_io_stop(loop->ev, &h->read_watcher); + ev_io_stop(loop->ev, &h->write_watcher); + + uv_remove_ares_handle(h); + free(h); + + if (uv_ares_handles_empty(loop)) { + ev_timer_stop(loop->ev, &loop->timer); + } + } +} + + +/* c-ares integration initialize and terminate */ +/* TODO: share this with windows? */ +int uv_ares_init_options(uv_loop_t* loop, ares_channel *channelptr, + struct ares_options *options, int optmask) { + int rc; + + /* only allow single init at a time */ + if (loop->channel != NULL) { + uv_err_new_artificial(loop, UV_EALREADY); + return -1; + } + + /* set our callback as an option */ + options->sock_state_cb = uv__ares_sockstate_cb; + options->sock_state_cb_data = loop; + optmask |= ARES_OPT_SOCK_STATE_CB; + + /* We do the call to ares_init_option for caller. */ + rc = ares_init_options(channelptr, options, optmask); + + /* if success, save channel */ + if (rc == ARES_SUCCESS) { + loop->channel = *channelptr; + } + + /* + * Initialize the timeout timer. The timer won't be started until the + * first socket is opened. + */ + ev_timer_init(&loop->timer, uv__ares_timeout, 1., 1.); + loop->timer.data = loop; + + return rc; +} + + +/* TODO share this with windows? */ +void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) { + /* only allow destroy if did init */ + if (loop->channel) { + ev_timer_stop(loop->ev, &loop->timer); + ares_destroy(channel); + loop->channel = NULL; + } +} diff --git a/src/rt/libuv/src/unix/core.c b/src/rt/libuv/src/unix/core.c new file mode 100644 index 00000000000..80b3bf6ed9e --- /dev/null +++ b/src/rt/libuv/src/unix/core.c @@ -0,0 +1,799 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "unix/internal.h" + +#include /* NULL */ +#include /* printf */ +#include +#include /* strerror */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* PATH_MAX */ +#include /* writev */ + +#ifdef __sun +# include +# include +#endif + +#if defined(__APPLE__) +#include /* _NSGetExecutablePath */ +#endif + +#if defined(__FreeBSD__) +#include +#include +#endif + +static uv_loop_t default_loop_struct; +static uv_loop_t* default_loop_ptr; + +void uv__next(EV_P_ ev_idle* watcher, int revents); +static void uv__finish_close(uv_handle_t* handle); + + + +#ifndef __GNUC__ +#define __attribute__(a) +#endif + + +void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { + uv_udp_t* udp; + uv_async_t* async; + uv_timer_t* timer; + uv_stream_t* stream; + uv_process_t* process; + + handle->close_cb = close_cb; + + switch (handle->type) { + case UV_NAMED_PIPE: + uv_pipe_cleanup((uv_pipe_t*)handle); + /* Fall through. */ + + case UV_TTY: + case UV_TCP: + stream = (uv_stream_t*)handle; + + uv_read_stop(stream); + ev_io_stop(stream->loop->ev, &stream->write_watcher); + + uv__close(stream->fd); + stream->fd = -1; + + if (stream->accepted_fd >= 0) { + uv__close(stream->accepted_fd); + stream->accepted_fd = -1; + } + + assert(!ev_is_active(&stream->read_watcher)); + assert(!ev_is_active(&stream->write_watcher)); + break; + + case UV_UDP: + udp = (uv_udp_t*)handle; + uv__udp_watcher_stop(udp, &udp->read_watcher); + uv__udp_watcher_stop(udp, &udp->write_watcher); + uv__close(udp->fd); + udp->fd = -1; + break; + + case UV_PREPARE: + uv_prepare_stop((uv_prepare_t*) handle); + break; + + case UV_CHECK: + uv_check_stop((uv_check_t*) handle); + break; + + case UV_IDLE: + uv_idle_stop((uv_idle_t*) handle); + break; + + case UV_ASYNC: + async = (uv_async_t*)handle; + ev_async_stop(async->loop->ev, &async->async_watcher); + ev_ref(async->loop->ev); + break; + + case UV_TIMER: + timer = (uv_timer_t*)handle; + if (ev_is_active(&timer->timer_watcher)) { + ev_ref(timer->loop->ev); + } + ev_timer_stop(timer->loop->ev, &timer->timer_watcher); + break; + + case UV_PROCESS: + process = (uv_process_t*)handle; + ev_child_stop(process->loop->ev, &process->child_watcher); + break; + + case UV_FS_EVENT: + uv__fs_event_destroy((uv_fs_event_t*)handle); + break; + + default: + assert(0); + } + + handle->flags |= UV_CLOSING; + + /* This is used to call the on_close callback in the next loop. */ + ev_idle_start(handle->loop->ev, &handle->next_watcher); + ev_feed_event(handle->loop->ev, &handle->next_watcher, EV_IDLE); + assert(ev_is_pending(&handle->next_watcher)); +} + + +uv_loop_t* uv_loop_new() { + uv_loop_t* loop = calloc(1, sizeof(uv_loop_t)); + loop->ev = ev_loop_new(0); + ev_set_userdata(loop->ev, loop); + return loop; +} + + +void uv_loop_delete(uv_loop_t* loop) { + uv_ares_destroy(loop, loop->channel); + ev_loop_destroy(loop->ev); + free(loop); +} + + +uv_loop_t* uv_default_loop() { + if (!default_loop_ptr) { + default_loop_ptr = &default_loop_struct; +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + default_loop_struct.ev = ev_default_loop(EVBACKEND_KQUEUE); +#else + default_loop_struct.ev = ev_default_loop(EVFLAG_AUTO); +#endif + ev_set_userdata(default_loop_struct.ev, default_loop_ptr); + } + assert(default_loop_ptr->ev == EV_DEFAULT_UC); + return default_loop_ptr; +} + + +int uv_run(uv_loop_t* loop) { + ev_run(loop->ev, 0); + return 0; +} + + +void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, + uv_handle_type type) { + loop->counters.handle_init++; + + handle->loop = loop; + handle->type = type; + handle->flags = 0; + + ev_init(&handle->next_watcher, uv__next); + handle->next_watcher.data = handle; + + /* Ref the loop until this handle is closed. See uv__finish_close. */ + ev_ref(loop->ev); +} + + +void uv__finish_close(uv_handle_t* handle) { + uv_loop_t* loop = handle->loop; + + assert(handle->flags & UV_CLOSING); + assert(!(handle->flags & UV_CLOSED)); + handle->flags |= UV_CLOSED; + + switch (handle->type) { + case UV_PREPARE: + assert(!ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher)); + break; + + case UV_CHECK: + assert(!ev_is_active(&((uv_check_t*)handle)->check_watcher)); + break; + + case UV_IDLE: + assert(!ev_is_active(&((uv_idle_t*)handle)->idle_watcher)); + break; + + case UV_ASYNC: + assert(!ev_is_active(&((uv_async_t*)handle)->async_watcher)); + break; + + case UV_TIMER: + assert(!ev_is_active(&((uv_timer_t*)handle)->timer_watcher)); + break; + + case UV_NAMED_PIPE: + case UV_TCP: + case UV_TTY: + assert(!ev_is_active(&((uv_stream_t*)handle)->read_watcher)); + assert(!ev_is_active(&((uv_stream_t*)handle)->write_watcher)); + assert(((uv_stream_t*)handle)->fd == -1); + uv__stream_destroy((uv_stream_t*)handle); + break; + + case UV_UDP: + assert(!ev_is_active(&((uv_udp_t*)handle)->read_watcher)); + assert(!ev_is_active(&((uv_udp_t*)handle)->write_watcher)); + assert(((uv_udp_t*)handle)->fd == -1); + uv__udp_destroy((uv_udp_t*)handle); + break; + + case UV_PROCESS: + assert(!ev_is_active(&((uv_process_t*)handle)->child_watcher)); + break; + + case UV_FS_EVENT: + break; + + default: + assert(0); + break; + } + + ev_idle_stop(loop->ev, &handle->next_watcher); + + if (handle->close_cb) { + handle->close_cb(handle); + } + + ev_unref(loop->ev); +} + + +void uv__next(EV_P_ ev_idle* watcher, int revents) { + uv_handle_t* handle = watcher->data; + assert(watcher == &handle->next_watcher); + assert(revents == EV_IDLE); + + /* For now this function is only to handle the closing event, but we might + * put more stuff here later. + */ + assert(handle->flags & UV_CLOSING); + uv__finish_close(handle); +} + + +void uv_ref(uv_loop_t* loop) { + ev_ref(loop->ev); +} + + +void uv_unref(uv_loop_t* loop) { + ev_unref(loop->ev); +} + + +void uv_update_time(uv_loop_t* loop) { + ev_now_update(loop->ev); +} + + +int64_t uv_now(uv_loop_t* loop) { + return (int64_t)(ev_now(loop->ev) * 1000); +} + + +void uv__req_init(uv_req_t* req) { + /* loop->counters.req_init++; */ + req->type = UV_UNKNOWN_REQ; +} + + +static void uv__prepare(EV_P_ ev_prepare* w, int revents) { + uv_prepare_t* prepare = w->data; + + if (prepare->prepare_cb) { + prepare->prepare_cb(prepare, 0); + } +} + + +int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare) { + uv__handle_init(loop, (uv_handle_t*)prepare, UV_PREPARE); + loop->counters.prepare_init++; + + ev_prepare_init(&prepare->prepare_watcher, uv__prepare); + prepare->prepare_watcher.data = prepare; + + prepare->prepare_cb = NULL; + + return 0; +} + + +int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) { + int was_active = ev_is_active(&prepare->prepare_watcher); + + prepare->prepare_cb = cb; + + ev_prepare_start(prepare->loop->ev, &prepare->prepare_watcher); + + if (!was_active) { + ev_unref(prepare->loop->ev); + } + + return 0; +} + + +int uv_prepare_stop(uv_prepare_t* prepare) { + int was_active = ev_is_active(&prepare->prepare_watcher); + + ev_prepare_stop(prepare->loop->ev, &prepare->prepare_watcher); + + if (was_active) { + ev_ref(prepare->loop->ev); + } + return 0; +} + + + +static void uv__check(EV_P_ ev_check* w, int revents) { + uv_check_t* check = w->data; + + if (check->check_cb) { + check->check_cb(check, 0); + } +} + + +int uv_check_init(uv_loop_t* loop, uv_check_t* check) { + uv__handle_init(loop, (uv_handle_t*)check, UV_CHECK); + loop->counters.check_init++; + + ev_check_init(&check->check_watcher, uv__check); + check->check_watcher.data = check; + + check->check_cb = NULL; + + return 0; +} + + +int uv_check_start(uv_check_t* check, uv_check_cb cb) { + int was_active = ev_is_active(&check->check_watcher); + + check->check_cb = cb; + + ev_check_start(check->loop->ev, &check->check_watcher); + + if (!was_active) { + ev_unref(check->loop->ev); + } + + return 0; +} + + +int uv_check_stop(uv_check_t* check) { + int was_active = ev_is_active(&check->check_watcher); + + ev_check_stop(check->loop->ev, &check->check_watcher); + + if (was_active) { + ev_ref(check->loop->ev); + } + + return 0; +} + + +static void uv__idle(EV_P_ ev_idle* w, int revents) { + uv_idle_t* idle = (uv_idle_t*)(w->data); + + if (idle->idle_cb) { + idle->idle_cb(idle, 0); + } +} + + + +int uv_idle_init(uv_loop_t* loop, uv_idle_t* idle) { + uv__handle_init(loop, (uv_handle_t*)idle, UV_IDLE); + loop->counters.idle_init++; + + ev_idle_init(&idle->idle_watcher, uv__idle); + idle->idle_watcher.data = idle; + + idle->idle_cb = NULL; + + return 0; +} + + +int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) { + int was_active = ev_is_active(&idle->idle_watcher); + + idle->idle_cb = cb; + ev_idle_start(idle->loop->ev, &idle->idle_watcher); + + if (!was_active) { + ev_unref(idle->loop->ev); + } + + return 0; +} + + +int uv_idle_stop(uv_idle_t* idle) { + int was_active = ev_is_active(&idle->idle_watcher); + + ev_idle_stop(idle->loop->ev, &idle->idle_watcher); + + if (was_active) { + ev_ref(idle->loop->ev); + } + + return 0; +} + + +int uv_is_active(uv_handle_t* handle) { + switch (handle->type) { + case UV_TIMER: + return ev_is_active(&((uv_timer_t*)handle)->timer_watcher); + + case UV_PREPARE: + return ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher); + + case UV_CHECK: + return ev_is_active(&((uv_check_t*)handle)->check_watcher); + + case UV_IDLE: + return ev_is_active(&((uv_idle_t*)handle)->idle_watcher); + + default: + return 1; + } +} + + +static void uv__async(EV_P_ ev_async* w, int revents) { + uv_async_t* async = w->data; + + if (async->async_cb) { + async->async_cb(async, 0); + } +} + + +int uv_async_init(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb) { + uv__handle_init(loop, (uv_handle_t*)async, UV_ASYNC); + loop->counters.async_init++; + + ev_async_init(&async->async_watcher, uv__async); + async->async_watcher.data = async; + + async->async_cb = async_cb; + + /* Note: This does not have symmetry with the other libev wrappers. */ + ev_async_start(loop->ev, &async->async_watcher); + ev_unref(loop->ev); + + return 0; +} + + +int uv_async_send(uv_async_t* async) { + ev_async_send(async->loop->ev, &async->async_watcher); + return 0; +} + + +static void uv__timer_cb(EV_P_ ev_timer* w, int revents) { + uv_timer_t* timer = w->data; + + if (!ev_is_active(w)) { + ev_ref(EV_A); + } + + if (timer->timer_cb) { + timer->timer_cb(timer, 0); + } +} + + +int uv_timer_init(uv_loop_t* loop, uv_timer_t* timer) { + uv__handle_init(loop, (uv_handle_t*)timer, UV_TIMER); + loop->counters.timer_init++; + + ev_init(&timer->timer_watcher, uv__timer_cb); + timer->timer_watcher.data = timer; + + return 0; +} + + +int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, + int64_t repeat) { + if (ev_is_active(&timer->timer_watcher)) { + return -1; + } + + timer->timer_cb = cb; + ev_timer_set(&timer->timer_watcher, timeout / 1000.0, repeat / 1000.0); + ev_timer_start(timer->loop->ev, &timer->timer_watcher); + ev_unref(timer->loop->ev); + return 0; +} + + +int uv_timer_stop(uv_timer_t* timer) { + if (ev_is_active(&timer->timer_watcher)) { + ev_ref(timer->loop->ev); + } + + ev_timer_stop(timer->loop->ev, &timer->timer_watcher); + return 0; +} + + +int uv_timer_again(uv_timer_t* timer) { + if (!ev_is_active(&timer->timer_watcher)) { + uv_err_new(timer->loop, EINVAL); + return -1; + } + + ev_timer_again(timer->loop->ev, &timer->timer_watcher); + return 0; +} + +void uv_timer_set_repeat(uv_timer_t* timer, int64_t repeat) { + assert(timer->type == UV_TIMER); + timer->timer_watcher.repeat = repeat / 1000.0; +} + +int64_t uv_timer_get_repeat(uv_timer_t* timer) { + assert(timer->type == UV_TIMER); + return (int64_t)(1000 * timer->timer_watcher.repeat); +} + + +static int uv_getaddrinfo_done(eio_req* req) { + uv_getaddrinfo_t* handle = req->data; + struct addrinfo *res = handle->res; + handle->res = NULL; + + uv_unref(handle->loop); + + free(handle->hints); + free(handle->service); + free(handle->hostname); + + if (handle->retcode != 0) { + /* TODO how to display gai error strings? */ + uv_err_new(handle->loop, handle->retcode); + } + + handle->cb(handle, handle->retcode, res); + + return 0; +} + + +static void getaddrinfo_thread_proc(eio_req *req) { + uv_getaddrinfo_t* handle = req->data; + + handle->retcode = getaddrinfo(handle->hostname, + handle->service, + handle->hints, + &handle->res); +} + + +/* stub implementation of uv_getaddrinfo */ +int uv_getaddrinfo(uv_loop_t* loop, + uv_getaddrinfo_t* handle, + uv_getaddrinfo_cb cb, + const char* hostname, + const char* service, + const struct addrinfo* hints) { + eio_req* req; + uv_eio_init(loop); + + if (handle == NULL || cb == NULL || + (hostname == NULL && service == NULL)) { + uv_err_new_artificial(loop, UV_EINVAL); + return -1; + } + + uv__req_init((uv_req_t*)handle); + handle->type = UV_GETADDRINFO; + handle->loop = loop; + handle->cb = cb; + + /* TODO don't alloc so much. */ + + if (hints) { + handle->hints = malloc(sizeof(struct addrinfo)); + memcpy(&handle->hints, hints, sizeof(struct addrinfo)); + } + else { + handle->hints = NULL; + } + + /* TODO security! check lengths, check return values. */ + + handle->hostname = hostname ? strdup(hostname) : NULL; + handle->service = service ? strdup(service) : NULL; + handle->res = NULL; + handle->retcode = 0; + + /* TODO check handle->hostname == NULL */ + /* TODO check handle->service == NULL */ + + uv_ref(loop); + + req = eio_custom(getaddrinfo_thread_proc, EIO_PRI_DEFAULT, + uv_getaddrinfo_done, handle); + assert(req); + assert(req->data == handle); + + return 0; +} + + +void uv_freeaddrinfo(struct addrinfo* ai) { + freeaddrinfo(ai); +} + + +/* Open a socket in non-blocking close-on-exec mode, atomically if possible. */ +int uv__socket(int domain, int type, int protocol) { +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) + return socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); +#else + int sockfd; + + if ((sockfd = socket(domain, type, protocol)) == -1) { + return -1; + } + + if (uv__nonblock(sockfd, 1) == -1 || uv__cloexec(sockfd, 1) == -1) { + uv__close(sockfd); + return -1; + } + + return sockfd; +#endif +} + + +int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t slen) { + int peerfd; + + assert(sockfd >= 0); + + do { +#if defined(HAVE_ACCEPT4) + peerfd = accept4(sockfd, saddr, &slen, SOCK_NONBLOCK | SOCK_CLOEXEC); +#else + if ((peerfd = accept(sockfd, saddr, &slen)) != -1) { + if (uv__cloexec(peerfd, 1) == -1 || uv__nonblock(peerfd, 1) == -1) { + uv__close(peerfd); + return -1; + } + } +#endif + } + while (peerfd == -1 && errno == EINTR); + + return peerfd; +} + + +int uv__close(int fd) { + int status; + + /* + * Retry on EINTR. You may think this is academic but on linux + * and probably other Unices too, close(2) is interruptible. + * Failing to handle EINTR is a common source of fd leaks. + */ + do { + status = close(fd); + } + while (status == -1 && errno == EINTR); + + return status; +} + + +int uv__nonblock(int fd, int set) { + int flags; + + if ((flags = fcntl(fd, F_GETFL)) == -1) { + return -1; + } + + if (set) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + + if (fcntl(fd, F_SETFL, flags) == -1) { + return -1; + } + + return 0; +} + + +int uv__cloexec(int fd, int set) { + int flags; + + if ((flags = fcntl(fd, F_GETFD)) == -1) { + return -1; + } + + if (set) { + flags |= FD_CLOEXEC; + } else { + flags &= ~FD_CLOEXEC; + } + + if (fcntl(fd, F_SETFD, flags) == -1) { + return -1; + } + + return 0; +} + + +/* TODO move to uv-common.c? */ +size_t uv__strlcpy(char* dst, const char* src, size_t size) { + const char *org; + + if (size == 0) { + return 0; + } + + org = src; + while (--size && *src) { + *dst++ = *src++; + } + *dst = '\0'; + + return src - org; +} + + +uv_stream_t* uv_std_handle(uv_loop_t* loop, uv_std_type type) { + assert(0 && "implement me"); + return NULL; +} + diff --git a/src/rt/libuv/src/uv-cygwin.c b/src/rt/libuv/src/unix/cygwin.c similarity index 83% rename from src/rt/libuv/src/uv-cygwin.c rename to src/rt/libuv/src/unix/cygwin.c index d56a8e839ba..6702eb9234f 100644 --- a/src/rt/libuv/src/uv-cygwin.c +++ b/src/rt/libuv/src/unix/cygwin.c @@ -20,8 +20,10 @@ #include "uv.h" +#include #include #include +#include #include #undef NANOSEC @@ -50,3 +52,17 @@ int uv_exepath(char* buffer, size_t* size) { buffer[*size] = '\0'; return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/uv-darwin.c b/src/rt/libuv/src/unix/darwin.c similarity index 82% rename from src/rt/libuv/src/uv-darwin.c rename to src/rt/libuv/src/unix/darwin.c index fa2948cd217..af72b4b0ec8 100644 --- a/src/rt/libuv/src/uv-darwin.c +++ b/src/rt/libuv/src/unix/darwin.c @@ -19,11 +19,16 @@ */ #include "uv.h" +#include "internal.h" +#include #include +#include + #include #include #include +#include /* _NSGetExecutablePath */ uint64_t uv_hrtime() { @@ -62,3 +67,17 @@ int uv_exepath(char* buffer, size_t* size) { *size = strlen(buffer); return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/eio/Changes b/src/rt/libuv/src/unix/eio/Changes new file mode 100644 index 00000000000..9d3e3231c12 --- /dev/null +++ b/src/rt/libuv/src/unix/eio/Changes @@ -0,0 +1,63 @@ +Revision history for libeio + +TODO: maybe add mincore support? available on at least darwin, solaris, linux, freebsd +TODO: openbsd requires stdint.h for intptr_t - why posix? + +TODO: make mtouch/readdir maybe others cancellable in-request +TODO: fadvise request +1.0 + - fix a deadlock where a wakeup signal could be missed when + a timeout occured at the same time. + - use nonstandard but maybe-working-on-bsd fork technique. + - use fewer time() syscalls when waiting for new requests. + - fix a path-memory-leak in readdir when using the wrappers + (reported by Thomas L. Shinnick). + - support a max_idle value of 0. + - support setting of idle timeout value (eio_set_idle_timeout). + - readdir: correctly handle malloc failures. + - readdir: new flags argument, can return inode + and possibly filetype, can sort in various ways. + - readdir: stop immediately when cancelled, do + not continue reading the directory. + - fix return value of eio_sendfile_sync. + - include sys/mman.h for msync. + - added EIO_STACKSIZE. + - added msync, mtouch support (untested). + - added sync_file_range (untested). + - fixed custom support. + - use a more robust feed-add detection method. + - "outbundled" from IO::AIO. + - eio_set_max_polltime did not properly convert time to ticks. + - tentatively support darwin in sendfile. + - fix freebsd/darwin sendfile. + - also use sendfile emulation for ENOTSUP and EOPNOTSUPP + error codes. + - add OS-independent EIO_MT_* and EIO_MS_* flag enums. + - add eio_statvfs/eio_fstatvfs. + - add eio_mlock/eio_mlockall and OS-independent MCL_* flag enums. + - no longer set errno to 0 before making syscalls, this only lures + people into the trap of believing errno shows success or failure. + - "fix" demo.c so that it works as non-root. + - suppoert utimes seperately from futimes, as some systems have + utimes but not futimes. + - use _POSIX_MEMLOCK_RANGE for mlock. + - do not (errornously) overwrite CFLAGS in configure.ac. + - mknod used int3 for dev_t (§2 bit), not offs (64 bit). + - fix memory corruption in eio_readdirx for the flags + combination EIO_READDIR_STAT_ORDER | EIO_READDIR_DIRS_FIRST. + - port to openbsd (another blatantly broken non-UNIX/POSIX platform). + - fix eio_custom prototype. + - work around a Linux (and likely FreeBSD and other kernels) bug + where sendfile would not transfer all the requested bytes on + large transfers, using a heuristic. + - use libecb, and apply lots of minor space optimisations. + - disable sendfile on darwin, broken as everything else. + - add realpath request and implementation. + - cancelled requests will still invoke their request callbacks. + - add fallocate. + - do not acquire any locks when forking. + - incorporated some mingw32 changes by traviscline. + - added syncfs support, using direct syscall. + - set thread name on linux (ps -L/Hcx, top, gdb). + - remove useless use of volatile variables. + diff --git a/src/rt/libuv/src/eio/LICENSE b/src/rt/libuv/src/unix/eio/LICENSE similarity index 100% rename from src/rt/libuv/src/eio/LICENSE rename to src/rt/libuv/src/unix/eio/LICENSE diff --git a/src/rt/libuv/src/eio/Makefile.am b/src/rt/libuv/src/unix/eio/Makefile.am similarity index 81% rename from src/rt/libuv/src/eio/Makefile.am rename to src/rt/libuv/src/unix/eio/Makefile.am index 857d26b6190..e9866c0d5e7 100644 --- a/src/rt/libuv/src/eio/Makefile.am +++ b/src/rt/libuv/src/unix/eio/Makefile.am @@ -10,6 +10,6 @@ include_HEADERS = eio.h lib_LTLIBRARIES = libeio.la -libeio_la_SOURCES = eio.c xthread.h config.h +libeio_la_SOURCES = eio.c ecb.h xthread.h config.h libeio_la_LDFLAGS = -version-info $(VERSION_INFO) diff --git a/src/rt/libuv/src/eio/aclocal.m4 b/src/rt/libuv/src/unix/eio/aclocal.m4 similarity index 100% rename from src/rt/libuv/src/eio/aclocal.m4 rename to src/rt/libuv/src/unix/eio/aclocal.m4 diff --git a/src/rt/libuv/src/unix/eio/autogen.sh b/src/rt/libuv/src/unix/eio/autogen.sh new file mode 100755 index 00000000000..8056ee7f9be --- /dev/null +++ b/src/rt/libuv/src/unix/eio/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +autoreconf --install --symlink --force diff --git a/src/rt/libuv/src/eio/config.h.in b/src/rt/libuv/src/unix/eio/config.h.in similarity index 100% rename from src/rt/libuv/src/eio/config.h.in rename to src/rt/libuv/src/unix/eio/config.h.in diff --git a/src/rt/libuv/src/eio/config_cygwin.h b/src/rt/libuv/src/unix/eio/config_cygwin.h similarity index 97% rename from src/rt/libuv/src/eio/config_cygwin.h rename to src/rt/libuv/src/unix/eio/config_cygwin.h index f149a6b352a..f64e7fe441a 100644 --- a/src/rt/libuv/src/eio/config_cygwin.h +++ b/src/rt/libuv/src/unix/eio/config_cygwin.h @@ -7,6 +7,9 @@ /* fdatasync(2) is available */ #define HAVE_FDATASYNC 1 +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ #define HAVE_FUTIMES 1 diff --git a/src/rt/libuv/src/unix/eio/config_darwin.h b/src/rt/libuv/src/unix/eio/config_darwin.h new file mode 100644 index 00000000000..f406759e09e --- /dev/null +++ b/src/rt/libuv/src/unix/eio/config_darwin.h @@ -0,0 +1,141 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* fallocate(2) is available */ +/* #undef HAVE_FALLOCATE */ + +/* fdatasync(2) is available */ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 +#define HAVE_FDATASYNC 1 +#else +#define HAVE_FDATASYNC 0 +#endif + +/* futimes(2) is available */ +#define HAVE_FUTIMES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* posix_fadvise(2) is available */ +/* #undef HAVE_POSIX_FADVISE */ + +/* posix_madvise(2) is available */ +#define HAVE_POSIX_MADVISE 1 + +/* prctl(PR_SET_NAME) is available */ +/* #undef HAVE_PRCTL_SET_NAME */ + +/* pread(2) and pwrite(2) are available */ +#define HAVE_PREADWRITE 1 + +/* readahead(2) is available (linux) */ +/* #undef HAVE_READAHEAD */ + +/* sendfile(2) is available and supported */ +#define HAVE_SENDFILE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* sync_file_range(2) is available */ +/* #undef HAVE_SYNC_FILE_RANGE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PRCTL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* syscall(__NR_syncfs) is available */ +/* #undef HAVE_SYS_SYNCFS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCALL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libeio" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "1.0" + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ diff --git a/src/rt/libuv/src/eio/config_freebsd.h b/src/rt/libuv/src/unix/eio/config_freebsd.h similarity index 97% rename from src/rt/libuv/src/eio/config_freebsd.h rename to src/rt/libuv/src/unix/eio/config_freebsd.h index b740cdd8787..3c93d260f79 100644 --- a/src/rt/libuv/src/eio/config_freebsd.h +++ b/src/rt/libuv/src/unix/eio/config_freebsd.h @@ -7,6 +7,9 @@ /* fdatasync(2) is available */ /* #undef HAVE_FDATASYNC */ +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ #define HAVE_FUTIMES 1 diff --git a/src/rt/libuv/src/eio/config_linux.h b/src/rt/libuv/src/unix/eio/config_linux.h similarity index 85% rename from src/rt/libuv/src/eio/config_linux.h rename to src/rt/libuv/src/unix/eio/config_linux.h index 9823f624085..606301faf2a 100644 --- a/src/rt/libuv/src/eio/config_linux.h +++ b/src/rt/libuv/src/unix/eio/config_linux.h @@ -2,12 +2,7 @@ /* config.h.in. Generated from configure.ac by autoheader. */ #include - -#define LINUX_VERSION_CODE_FOR(major, minor, patch) \ - (((major & 255) << 16) | ((minor & 255) << 8) | (patch & 255)) - -#define LINUX_VERSION_AT_LEAST(major, minor, patch) \ - (LINUX_VERSION_CODE >= LINUX_VERSION_CODE_FOR(major, minor, patch)) +#include /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 @@ -15,6 +10,9 @@ /* fdatasync(2) is available */ #define HAVE_FDATASYNC 1 +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ #define HAVE_FUTIMES 1 @@ -45,8 +43,12 @@ /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 -/* sync_file_range(2) is available */ -#define HAVE_SYNC_FILE_RANGE LINUX_VERSION_AT_LEAST(2, 6, 17) +/* sync_file_range(2) is available if kernel >= 2.6.17 and glibc >= 2.6 */ +#if LINUX_VERSION_CODE >= 0x020611 && __GLIBC_PREREQ(2, 6) +#define HAVE_SYNC_FILE_RANGE 1 +#else +#define HAVE_SYNC_FILE_RANGE 0 +#endif /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 diff --git a/src/rt/libuv/src/eio/config_darwin.h b/src/rt/libuv/src/unix/eio/config_netbsd.h similarity index 90% rename from src/rt/libuv/src/eio/config_darwin.h rename to src/rt/libuv/src/unix/eio/config_netbsd.h index 84a3440df0d..31f18e60465 100644 --- a/src/rt/libuv/src/eio/config_darwin.h +++ b/src/rt/libuv/src/unix/eio/config_netbsd.h @@ -4,9 +4,11 @@ /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 -/* fdatasync(2) is not available on 10.5 but is on 10.6 - * How should we deal with this? */ -/* #define HAVE_FDATASYNC 0 */ +/* fdatasync(2) is available */ +/* #undef HAVE_FDATASYNC */ + +/* utimes(2) is available */ +#define HAVE_UTIMES 1 /* futimes(2) is available */ #define HAVE_FUTIMES 1 @@ -24,7 +26,7 @@ /* #undef HAVE_READAHEAD */ /* sendfile(2) is available and supported */ -#define HAVE_SENDFILE 1 +#define HAVE_SENDFILE 0 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 @@ -69,9 +71,6 @@ /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "" -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - /* Define to the version of this package. */ #define PACKAGE_VERSION "" diff --git a/src/rt/libuv/src/eio/config_sunos.h b/src/rt/libuv/src/unix/eio/config_sunos.h similarity index 97% rename from src/rt/libuv/src/eio/config_sunos.h rename to src/rt/libuv/src/unix/eio/config_sunos.h index 8f878efd956..01d049c3e62 100644 --- a/src/rt/libuv/src/eio/config_sunos.h +++ b/src/rt/libuv/src/unix/eio/config_sunos.h @@ -7,6 +7,9 @@ /* fdatasync(2) is available */ #define HAVE_FDATASYNC 1 +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ /* #undef HAVE_FUTIMES */ diff --git a/src/rt/libuv/src/eio/configure.ac b/src/rt/libuv/src/unix/eio/configure.ac similarity index 75% rename from src/rt/libuv/src/eio/configure.ac rename to src/rt/libuv/src/unix/eio/configure.ac index 9f4cea9d12d..9faffad538b 100644 --- a/src/rt/libuv/src/eio/configure.ac +++ b/src/rt/libuv/src/unix/eio/configure.ac @@ -5,17 +5,17 @@ AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE(libeio,1.0) AM_MAINTAINER_MODE + +AC_GNU_SOURCE + AC_PROG_LIBTOOL AC_PROG_CC if test "x$GCC" = xyes ; then - CFLAGS="$CFLAGS -O3" + CFLAGS="-O3 $CFLAGS" fi -dnl somebody will forgive me -CFLAGS="-D_GNU_SOURCE $CFLAGS" - m4_include([libeio.m4]) AC_CONFIG_FILES([Makefile]) diff --git a/src/rt/libuv/src/eio/demo.c b/src/rt/libuv/src/unix/eio/demo.c similarity index 100% rename from src/rt/libuv/src/eio/demo.c rename to src/rt/libuv/src/unix/eio/demo.c diff --git a/src/rt/libuv/src/unix/eio/ecb.h b/src/rt/libuv/src/unix/eio/ecb.h new file mode 100644 index 00000000000..a4aabc10916 --- /dev/null +++ b/src/rt/libuv/src/unix/eio/ecb.h @@ -0,0 +1,370 @@ +/* + * libecb - http://software.schmorp.de/pkg/libecb + * + * Copyright (©) 2009-2011 Marc Alexander Lehmann + * Copyright (©) 2011 Emanuele Giaquinta + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ECB_H +#define ECB_H + +#ifdef _WIN32 + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef signed short int16_t; + typedef unsigned short uint16_t; + typedef signed int int32_t; + typedef unsigned int uint32_t; + #if __GNUC__ + typedef signed long long int64_t; + typedef unsigned long long uint64_t; + #else /* _MSC_VER || __BORLANDC__ */ + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + #endif +#else + #include +#endif + +/* many compilers define _GNUC_ to some versions but then only implement + * what their idiot authors think are the "more important" extensions, + * causing enourmous grief in return for some better fake benchmark numbers. + * or so. + * we try to detect these and simply assume they are not gcc - if they have + * an issue with that they should have done it right in the first place. + */ +#ifndef ECB_GCC_VERSION + #if !defined(__GNUC_MINOR__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__llvm__) || defined(__clang__) + #define ECB_GCC_VERSION(major,minor) 0 + #else + #define ECB_GCC_VERSION(major,minor) (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) + #endif +#endif + +/*****************************************************************************/ + +#ifndef ECB_MEMORY_FENCE + #if ECB_GCC_VERSION(2,5) + #if defined(__x86) || defined(__i386) + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("lock; orb $0, -1(%%esp)" : : : "memory") + #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE /* non-lock xchg might be enough */ + #define ECB_MEMORY_FENCE_RELEASE do { } while (0) /* unlikely to change in future cpus */ + #elif __amd64 + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mfence" : : : "memory") + #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("lfence" : : : "memory") + #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("sfence") /* play safe - not needed in any current cpu */ + #endif + #endif +#endif + +#ifndef ECB_MEMORY_FENCE + #if ECB_GCC_VERSION(4,4) + #define ECB_MEMORY_FENCE __sync_synchronize () + #define ECB_MEMORY_FENCE_ACQUIRE ({ char dummy = 0; __sync_lock_test_and_set (&dummy, 1); }) + #define ECB_MEMORY_FENCE_RELEASE ({ char dummy = 1; __sync_lock_release (&dummy ); }) + #elif _MSC_VER >= 1400 /* VC++ 2005 */ + #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier) + #define ECB_MEMORY_FENCE _ReadWriteBarrier () + #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier () /* according to msdn, _ReadBarrier is not a load fence */ + #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier () + #elif defined(_WIN32) + #include + #define ECB_MEMORY_FENCE MemoryBarrier () /* actually just xchg on x86... scary */ + #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE + #define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE + #endif +#endif + +#ifndef ECB_MEMORY_FENCE + /* + * if you get undefined symbol references to pthread_mutex_lock, + * or failure to find pthread.h, then you should implement + * the ECB_MEMORY_FENCE operations for your cpu/compiler + * OR proide pthread.h and link against the posix thread library + * of your system. + */ + #include + + static pthread_mutex_t ecb_mf_lock = PTHREAD_MUTEX_INITIALIZER; + #define ECB_MEMORY_FENCE do { pthread_mutex_lock (&ecb_mf_lock); pthread_mutex_unlock (&ecb_mf_lock); } while (0) + #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE + #define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE +#endif + +/*****************************************************************************/ + +#define ECB_C99 (__STDC_VERSION__ >= 199901L) + +#if __cplusplus + #define ecb_inline static inline +#elif ECB_GCC_VERSION(2,5) + #define ecb_inline static __inline__ +#elif ECB_C99 + #define ecb_inline static inline +#else + #define ecb_inline static +#endif + +#if ECB_GCC_VERSION(3,3) + #define ecb_restrict __restrict__ +#elif ECB_C99 + #define ecb_restrict restrict +#else + #define ecb_restrict +#endif + +typedef int ecb_bool; + +#define ECB_CONCAT_(a, b) a ## b +#define ECB_CONCAT(a, b) ECB_CONCAT_(a, b) +#define ECB_STRINGIFY_(a) # a +#define ECB_STRINGIFY(a) ECB_STRINGIFY_(a) + +#define ecb_function_ ecb_inline + +#if ECB_GCC_VERSION(3,1) + #define ecb_attribute(attrlist) __attribute__(attrlist) + #define ecb_is_constant(expr) __builtin_constant_p (expr) + #define ecb_expect(expr,value) __builtin_expect ((expr),(value)) + #define ecb_prefetch(addr,rw,locality) __builtin_prefetch (addr, rw, locality) +#else + #define ecb_attribute(attrlist) + #define ecb_is_constant(expr) 0 + #define ecb_expect(expr,value) (expr) + #define ecb_prefetch(addr,rw,locality) +#endif + +/* no emulation for ecb_decltype */ +#if ECB_GCC_VERSION(4,5) + #define ecb_decltype(x) __decltype(x) +#elif ECB_GCC_VERSION(3,0) + #define ecb_decltype(x) __typeof(x) +#endif + +#define ecb_noinline ecb_attribute ((__noinline__)) +#define ecb_noreturn ecb_attribute ((__noreturn__)) +#define ecb_unused ecb_attribute ((__unused__)) +#define ecb_const ecb_attribute ((__const__)) +#define ecb_pure ecb_attribute ((__pure__)) + +#if ECB_GCC_VERSION(4,3) + #define ecb_artificial ecb_attribute ((__artificial__)) + #define ecb_hot ecb_attribute ((__hot__)) + #define ecb_cold ecb_attribute ((__cold__)) +#else + #define ecb_artificial + #define ecb_hot + #define ecb_cold +#endif + +/* put around conditional expressions if you are very sure that the */ +/* expression is mostly true or mostly false. note that these return */ +/* booleans, not the expression. */ +#define ecb_expect_false(expr) ecb_expect (!!(expr), 0) +#define ecb_expect_true(expr) ecb_expect (!!(expr), 1) +/* for compatibility to the rest of the world */ +#define ecb_likely(expr) ecb_expect_true (expr) +#define ecb_unlikely(expr) ecb_expect_false (expr) + +/* count trailing zero bits and count # of one bits */ +#if ECB_GCC_VERSION(3,4) + /* we assume int == 32 bit, long == 32 or 64 bit and long long == 64 bit */ + #define ecb_ld32(x) (__builtin_clz (x) ^ 31) + #define ecb_ld64(x) (__builtin_clzll (x) ^ 63) + #define ecb_ctz32(x) __builtin_ctz (x) + #define ecb_ctz64(x) __builtin_ctzll (x) + #define ecb_popcount32(x) __builtin_popcount (x) + /* no popcountll */ +#else + ecb_function_ int ecb_ctz32 (uint32_t x) ecb_const; + ecb_function_ int + ecb_ctz32 (uint32_t x) + { + int r = 0; + + x &= ~x + 1; /* this isolates the lowest bit */ + +#if ECB_branchless_on_i386 + r += !!(x & 0xaaaaaaaa) << 0; + r += !!(x & 0xcccccccc) << 1; + r += !!(x & 0xf0f0f0f0) << 2; + r += !!(x & 0xff00ff00) << 3; + r += !!(x & 0xffff0000) << 4; +#else + if (x & 0xaaaaaaaa) r += 1; + if (x & 0xcccccccc) r += 2; + if (x & 0xf0f0f0f0) r += 4; + if (x & 0xff00ff00) r += 8; + if (x & 0xffff0000) r += 16; +#endif + + return r; + } + + ecb_function_ int ecb_ctz64 (uint64_t x) ecb_const; + ecb_function_ int + ecb_ctz64 (uint64_t x) + { + int shift = x & 0xffffffffU ? 0 : 32; + return ecb_ctz32 (x >> shift) + shift; + } + + ecb_function_ int ecb_popcount32 (uint32_t x) ecb_const; + ecb_function_ int + ecb_popcount32 (uint32_t x) + { + x -= (x >> 1) & 0x55555555; + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + x = ((x >> 4) + x) & 0x0f0f0f0f; + x *= 0x01010101; + + return x >> 24; + } + + /* you have the choice beetween something with a table lookup, */ + /* something using lots of bit arithmetic and a simple loop */ + /* we went for the loop */ + ecb_function_ int ecb_ld32 (uint32_t x) ecb_const; + ecb_function_ int ecb_ld32 (uint32_t x) + { + int r = 0; + + if (x >> 16) { x >>= 16; r += 16; } + if (x >> 8) { x >>= 8; r += 8; } + if (x >> 4) { x >>= 4; r += 4; } + if (x >> 2) { x >>= 2; r += 2; } + if (x >> 1) { r += 1; } + + return r; + } + + ecb_function_ int ecb_ld64 (uint64_t x) ecb_const; + ecb_function_ int ecb_ld64 (uint64_t x) + { + int r = 0; + + if (x >> 32) { x >>= 32; r += 32; } + + return r + ecb_ld32 (x); + } +#endif + +/* popcount64 is only available on 64 bit cpus as gcc builtin */ +/* so for this version we are lazy */ +ecb_function_ int ecb_popcount64 (uint64_t x) ecb_const; +ecb_function_ int +ecb_popcount64 (uint64_t x) +{ + return ecb_popcount32 (x) + ecb_popcount32 (x >> 32); +} + +ecb_inline uint8_t ecb_rotl8 (uint8_t x, unsigned int count) ecb_const; +ecb_inline uint8_t ecb_rotr8 (uint8_t x, unsigned int count) ecb_const; +ecb_inline uint16_t ecb_rotl16 (uint16_t x, unsigned int count) ecb_const; +ecb_inline uint16_t ecb_rotr16 (uint16_t x, unsigned int count) ecb_const; +ecb_inline uint32_t ecb_rotl32 (uint32_t x, unsigned int count) ecb_const; +ecb_inline uint32_t ecb_rotr32 (uint32_t x, unsigned int count) ecb_const; +ecb_inline uint64_t ecb_rotl64 (uint64_t x, unsigned int count) ecb_const; +ecb_inline uint64_t ecb_rotr64 (uint64_t x, unsigned int count) ecb_const; + +ecb_inline uint8_t ecb_rotl8 (uint8_t x, unsigned int count) { return (x >> ( 8 - count)) | (x << count); } +ecb_inline uint8_t ecb_rotr8 (uint8_t x, unsigned int count) { return (x << ( 8 - count)) | (x >> count); } +ecb_inline uint16_t ecb_rotl16 (uint16_t x, unsigned int count) { return (x >> (16 - count)) | (x << count); } +ecb_inline uint16_t ecb_rotr16 (uint16_t x, unsigned int count) { return (x << (16 - count)) | (x >> count); } +ecb_inline uint32_t ecb_rotl32 (uint32_t x, unsigned int count) { return (x >> (32 - count)) | (x << count); } +ecb_inline uint32_t ecb_rotr32 (uint32_t x, unsigned int count) { return (x << (32 - count)) | (x >> count); } +ecb_inline uint64_t ecb_rotl64 (uint64_t x, unsigned int count) { return (x >> (64 - count)) | (x << count); } +ecb_inline uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { return (x << (64 - count)) | (x >> count); } + +#if ECB_GCC_VERSION(4,3) + #define ecb_bswap16(x) (__builtin_bswap32 (x) >> 16) + #define ecb_bswap32(x) __builtin_bswap32 (x) + #define ecb_bswap64(x) __builtin_bswap64 (x) +#else + ecb_function_ uint16_t ecb_bswap16 (uint16_t x) ecb_const; + ecb_function_ uint16_t + ecb_bswap16 (uint16_t x) + { + return ecb_rotl16 (x, 8); + } + + ecb_function_ uint32_t ecb_bswap32 (uint32_t x) ecb_const; + ecb_function_ uint32_t + ecb_bswap32 (uint32_t x) + { + return (((uint32_t)ecb_bswap16 (x)) << 16) | ecb_bswap16 (x >> 16); + } + + ecb_function_ uint64_t ecb_bswap64 (uint64_t x) ecb_const; + ecb_function_ uint64_t + ecb_bswap64 (uint64_t x) + { + return (((uint64_t)ecb_bswap32 (x)) << 32) | ecb_bswap32 (x >> 32); + } +#endif + +#if ECB_GCC_VERSION(4,5) + #define ecb_unreachable() __builtin_unreachable () +#else + /* this seems to work fine, but gcc always emits a warning for it :/ */ + ecb_function_ void ecb_unreachable (void) ecb_noreturn; + ecb_function_ void ecb_unreachable (void) { } +#endif + +/* try to tell the compiler that some condition is definitely true */ +#define ecb_assume(cond) do { if (!(cond)) ecb_unreachable (); } while (0) + +ecb_function_ unsigned char ecb_byteorder_helper (void) ecb_const; +ecb_function_ unsigned char +ecb_byteorder_helper (void) +{ + const uint32_t u = 0x11223344; + return *(unsigned char *)&u; +} + +ecb_function_ ecb_bool ecb_big_endian (void) ecb_const; +ecb_function_ ecb_bool ecb_big_endian (void) { return ecb_byteorder_helper () == 0x11; } +ecb_function_ ecb_bool ecb_little_endian (void) ecb_const; +ecb_function_ ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44; } + +#if ECB_GCC_VERSION(3,0) || ECB_C99 + #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0)) +#else + #define ecb_mod(m,n) ((m) < 0 ? ((n) - 1 - ((-1 - (m)) % (n))) : ((m) % (n))) +#endif + +#if ecb_cplusplus_does_not_suck + /* does not work for local types (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm) */ + template + static inline int ecb_array_length (const T (&arr)[N]) + { + return N; + } +#else + #define ecb_array_length(name) (sizeof (name) / sizeof (name [0])) +#endif + +#endif + diff --git a/src/rt/libuv/src/eio/eio.3 b/src/rt/libuv/src/unix/eio/eio.3 similarity index 100% rename from src/rt/libuv/src/eio/eio.3 rename to src/rt/libuv/src/unix/eio/eio.3 diff --git a/src/rt/libuv/src/eio/eio.c b/src/rt/libuv/src/unix/eio/eio.c similarity index 63% rename from src/rt/libuv/src/eio/eio.c rename to src/rt/libuv/src/unix/eio/eio.c index 7f48add4019..a005db5d3b4 100644 --- a/src/rt/libuv/src/eio/eio.c +++ b/src/rt/libuv/src/unix/eio/eio.c @@ -1,7 +1,7 @@ /* * libeio implementation * - * Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann + * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- @@ -37,16 +37,22 @@ * either the BSD or the GPL. */ -#include "eio.h" - -#ifdef EIO_STACKSIZE -# define XTHREAD_STACKSIZE EIO_STACKSIZE +#ifdef EIO_CONFIG_H +# include EIO_CONFIG_H #endif -// For statically-linked pthreads-w32, use: -// #ifdef _WIN32 -// # define PTW32_STATIC_LIB 1 -// #endif +/* Undone by libuv for easy build scripts. + * #ifndef _WIN32 + * # include "config.h" + * #endif + */ + +#include "eio.h" +#include "ecb.h" + +#ifdef EIO_STACKSIZE +# define X_STACKSIZE EIO_STACKSIZE +#endif #include "xthread.h" #include @@ -60,10 +66,25 @@ #include #include -#ifndef _WIN32 -#include +/* intptr_t comes from unistd.h, says POSIX/UNIX/tradition */ +/* intptr_t only comes from stdint.h, says idiot openbsd coder */ +#if HAVE_STDINT_H +# include #endif +#ifndef ECANCELED +# define ECANCELED EDOM +#endif +#ifndef ELOOP +# define ELOOP EDOM +#endif + +#if !defined(ENOTSOCK) && defined(WSAENOTSOCK) +# define ENOTSOCK WSAENOTSOCK +#endif + +static void eio_destroy (eio_req *req); + #ifndef EIO_FINISH # define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0 #endif @@ -76,65 +97,216 @@ # define EIO_FEED(req) do { if ((req)->feed ) (req)->feed (req); } while (0) #endif +#ifndef EIO_FD_TO_WIN32_HANDLE +# define EIO_FD_TO_WIN32_HANDLE(fd) _get_osfhandle (fd) +#endif +#ifndef EIO_WIN32_HANDLE_TO_FD +# define EIO_WIN32_HANDLE_TO_FD(handle) _open_osfhandle (handle, 0) +#endif + +#define EIO_ERRNO(errval,retval) ((errno = errval), retval) + +#define EIO_ENOSYS() EIO_ERRNO (ENOSYS, -1) + #ifdef _WIN32 -# include -# include -# include -# include -# include -# include -# include + #include -# define ENOTSOCK WSAENOTSOCK -# define EOPNOTSUPP WSAEOPNOTSUPP -# define ECANCELED 140 + #undef PAGESIZE + #define PAGESIZE 4096 /* GetSystemInfo? */ -# ifndef EIO_STRUCT_DIRENT -# define EIO_STRUCT_DIRENT struct dirent -# endif + /* TODO: look at how perl does stat (non-sloppy), unlink (ro-files), utime, link */ + + #ifdef EIO_STRUCT_STATI64 + /* look at perl's non-sloppy stat */ + #define stat(path,buf) _stati64 (path,buf) + #define fstat(fd,buf) _fstati64 (fd,buf) + #endif + #define lstat(path,buf) stat (path,buf) + #define fsync(fd) (FlushFileBuffers ((HANDLE)EIO_FD_TO_WIN32_HANDLE (fd)) ? 0 : EIO_ERRNO (EBADF, -1)) + #define mkdir(path,mode) _mkdir (path) + #define link(old,neu) (CreateHardLink (neu, old, 0) ? 0 : EIO_ERRNO (ENOENT, -1)) + + #define chmod(path,mode) _chmod (path, mode) + #define dup(fd) _dup (fd) + #define dup2(fd1,fd2) _dup2 (fd1, fd2) + + #define fchmod(fd,mode) EIO_ENOSYS () + #define chown(path,uid,gid) EIO_ENOSYS () + #define fchown(fd,uid,gid) EIO_ENOSYS () + #define truncate(path,offs) EIO_ENOSYS () /* far-miss: SetEndOfFile */ + #define ftruncate(fd,offs) EIO_ENOSYS () /* near-miss: SetEndOfFile */ + #define mknod(path,mode,dev) EIO_ENOSYS () + #define sync() EIO_ENOSYS () + #define readlink(path,buf,s) EIO_ENOSYS () + #define statvfs(path,buf) EIO_ENOSYS () + #define fstatvfs(fd,buf) EIO_ENOSYS () + + #define getcwd(buf,s) _getcwd(buf, s) + #define rmdir(path) _rmdir(path) + + /* rename() uses MoveFile, which fails to overwrite */ + #define rename(old,neu) eio__rename (old, neu) + + static int + eio__rename (const char *old, const char *neu) + { + if (MoveFileEx (old, neu, MOVEFILE_REPLACE_EXISTING)) + return 0; + + /* should steal _dosmaperr */ + switch (GetLastError ()) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_NO_MORE_FILES: + case ERROR_BAD_NETPATH: + case ERROR_BAD_NET_NAME: + case ERROR_BAD_PATHNAME: + case ERROR_FILENAME_EXCED_RANGE: + errno = ENOENT; + break; + + default: + errno = EACCES; + break; + } + + return -1; + } + + /* we could even stat and see if it exists */ + static int + symlink (const char *old, const char *neu) + { + #if WINVER >= 0x0600 + if (CreateSymbolicLink (neu, old, 1)) + return 0; + + if (CreateSymbolicLink (neu, old, 0)) + return 0; + #endif + + return EIO_ERRNO (ENOENT, -1); + } + + /* POSIX API only */ + #ifndef CreateHardLink + # define CreateHardLink(neu,old,flags) 0 + #endif + #define CreateSymbolicLink(neu,old,flags) 0 + + struct statvfs + { + int dummy; + }; + + #define DT_DIR EIO_DT_DIR + #define DT_REG EIO_DT_REG + #define D_NAME(entp) entp.cFileName + #define D_TYPE(entp) (entp.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? DT_DIR : DT_REG) + +#include +#define utime(path, times) _utime(path, times) +#define utimbuf _utimbuf + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +static int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tmpres /= 10; /*convert into microseconds*/ + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} #else -# ifdef EIO_CONFIG_H -# include EIO_CONFIG_H -# else -# include "config.h" -# endif + #include + #include + #include + #include + #include + #include -# include -# include -# include + #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES + #include + #endif + + #define D_NAME(entp) entp->d_name + + /* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ + #if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ + #define _DIRENT_HAVE_D_TYPE /* sigh */ + #define D_INO(de) (de)->d_fileno + #define D_NAMLEN(de) (de)->d_namlen + #elif __linux || defined d_ino || _XOPEN_SOURCE >= 600 + #define D_INO(de) (de)->d_ino + #endif + + #ifdef _D_EXACT_NAMLEN + #undef D_NAMLEN + #define D_NAMLEN(de) _D_EXACT_NAMLEN (de) + #endif + + #ifdef _DIRENT_HAVE_D_TYPE + #define D_TYPE(de) (de)->d_type + #endif + + #ifndef EIO_STRUCT_DIRENT + #define EIO_STRUCT_DIRENT struct dirent + #endif + +#endif + +#if HAVE_UTIMES # include -# include -# include - -#if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES -# include #endif -/* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ -# if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ -# define _DIRENT_HAVE_D_TYPE /* sigh */ -# define D_INO(de) (de)->d_fileno -# define D_NAMLEN(de) (de)->d_namlen -# elif __linux || defined d_ino || _XOPEN_SOURCE >= 600 -# define D_INO(de) (de)->d_ino -# endif - -#ifdef _D_EXACT_NAMLEN -# undef D_NAMLEN -# define D_NAMLEN(de) _D_EXACT_NAMLEN (de) +#if HAVE_SYS_SYSCALL_H +# include #endif -# ifdef _DIRENT_HAVE_D_TYPE -# define D_TYPE(de) (de)->d_type -# endif - -# ifndef EIO_STRUCT_DIRENT -# define EIO_STRUCT_DIRENT struct dirent -# endif - +#if HAVE_SYS_PRCTL_H +# include #endif #if HAVE_SENDFILE @@ -159,12 +331,9 @@ # define D_INO(de) 0 #endif #ifndef D_NAMLEN -# define D_NAMLEN(de) strlen ((de)->d_name) +# define D_NAMLEN(entp) strlen (D_NAME (entp)) #endif -/* number of seconds after which an idle threads exit */ -#define IDLE_TIMEOUT 10 - /* used for struct dirent, AIX doesn't provide it */ #ifndef NAME_MAX # define NAME_MAX 4096 @@ -179,29 +348,16 @@ #define EIO_BUFSIZE 65536 #define dBUF \ - char *eio_buf; \ - ETP_WORKER_LOCK (self); \ - self->dbuf = eio_buf = malloc (EIO_BUFSIZE); \ - ETP_WORKER_UNLOCK (self); \ + char *eio_buf = malloc (EIO_BUFSIZE); \ errno = ENOMEM; \ if (!eio_buf) \ - return -1; + return -1 + +#define FUBd \ + free (eio_buf) #define EIO_TICKS ((1000000 + 1023) >> 10) -/*****************************************************************************/ - -#if __GNUC__ >= 3 -# define expect(expr,value) __builtin_expect ((expr),(value)) -#else -# define expect(expr,value) (expr) -#endif - -#define expect_false(expr) expect ((expr) != 0, 0) -#define expect_true(expr) expect ((expr) != 0, 1) - -/*****************************************************************************/ - #define ETP_PRI_MIN EIO_PRI_MIN #define ETP_PRI_MAX EIO_PRI_MAX @@ -214,29 +370,13 @@ static int eio_finish (eio_req *req); static void eio_execute (struct etp_worker *self, eio_req *req); #define ETP_EXECUTE(wrk,req) eio_execute (wrk,req) -#define ETP_WORKER_CLEAR(req) \ - if (wrk->dbuf) \ - { \ - free (wrk->dbuf); \ - wrk->dbuf = 0; \ - } \ - \ - if (wrk->dirp) \ - { \ - closedir (wrk->dirp); \ - wrk->dirp = 0; \ - } - -#define ETP_WORKER_COMMON \ - void *dbuf; \ - DIR *dirp; - /*****************************************************************************/ #define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) /* calculate time difference in ~1/EIO_TICKS of a second */ -static int tvdiff (struct timeval *tv1, struct timeval *tv2) +ecb_inline int +tvdiff (struct timeval *tv1, struct timeval *tv2) { return (tv2->tv_sec - tv1->tv_sec ) * EIO_TICKS + ((tv2->tv_usec - tv1->tv_usec) >> 10); @@ -250,16 +390,23 @@ static void (*done_poll_cb) (void); static unsigned int max_poll_time; /* reslock */ static unsigned int max_poll_reqs; /* reslock */ -static volatile unsigned int nreqs; /* reqlock */ -static volatile unsigned int nready; /* reqlock */ -static volatile unsigned int npending; /* reqlock */ -static volatile unsigned int max_idle = 4; +static unsigned int nreqs; /* reqlock */ +static unsigned int nready; /* reqlock */ +static unsigned int npending; /* reqlock */ +static unsigned int max_idle = 4; /* maximum number of threads that can idle indefinitely */ +static unsigned int idle_timeout = 10; /* number of seconds after which an idle threads exit */ -static xmutex_t wrklock = X_MUTEX_INIT; -static xmutex_t reslock = X_MUTEX_INIT; -static xmutex_t reqlock = X_MUTEX_INIT; -static xcond_t reqwait = X_COND_INIT; +static xmutex_t wrklock; +static xmutex_t reslock; +static xmutex_t reqlock; +static xcond_t reqwait; +/* Fix for test-fs-sir-writes-alot */ +/* Apple's OSX can't safely write() concurrently from 2 threads */ +/* for more info see the thread "fs.write Data Munging" in the nodejs google group */ +/* http://groups.google.com/group/nodejs/browse_thread/thread/c11f8b683f37cef/b18ad9e0a15314c5 */ +/* And the thread "write()s and pwrite()s from multiple threads in OSX" in libev@lists.schmorp.de */ +/* http://lists.schmorp.de/pipermail/libev/2010q4/001185.html */ #if defined (__APPLE__) static xmutex_t apple_bug_writelock = X_MUTEX_INIT; #endif @@ -270,7 +417,7 @@ static xmutex_t apple_bug_writelock = X_MUTEX_INIT; * normal read/write by using a mutex. slows down execution a lot, * but that's your problem, not mine. */ -static xmutex_t preadwritelock = X_MUTEX_INIT; +static xmutex_t preadwritelock; #endif typedef struct etp_worker @@ -283,22 +430,25 @@ typedef struct etp_worker /* locked by reslock, reqlock or wrklock */ ETP_REQ *req; /* currently processed request */ +#ifdef ETP_WORKER_COMMON ETP_WORKER_COMMON +#endif } etp_worker; -static etp_worker wrk_first = { &wrk_first, &wrk_first, 0 }; /* NOT etp */ +static etp_worker wrk_first; /* NOT etp */ #define ETP_WORKER_LOCK(wrk) X_LOCK (wrklock) #define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock) /* worker threads management */ -static void etp_worker_clear (etp_worker *wrk) +static void ecb_cold +etp_worker_clear (etp_worker *wrk) { - ETP_WORKER_CLEAR (wrk); } -static void etp_worker_free (etp_worker *wrk) +static void ecb_cold +etp_worker_free (etp_worker *wrk) { wrk->next->prev = wrk->prev; wrk->prev->next = wrk->next; @@ -306,7 +456,8 @@ static void etp_worker_free (etp_worker *wrk) free (wrk); } -static unsigned int etp_nreqs (void) +static unsigned int +etp_nreqs (void) { int retval; if (WORDACCESS_UNSAFE) X_LOCK (reqlock); @@ -315,7 +466,8 @@ static unsigned int etp_nreqs (void) return retval; } -static unsigned int etp_nready (void) +static unsigned int +etp_nready (void) { unsigned int retval; @@ -326,7 +478,8 @@ static unsigned int etp_nready (void) return retval; } -static unsigned int etp_npending (void) +static unsigned int +etp_npending (void) { unsigned int retval; @@ -337,7 +490,8 @@ static unsigned int etp_npending (void) return retval; } -static unsigned int etp_nthreads (void) +static unsigned int +etp_nthreads (void) { unsigned int retval; @@ -361,7 +515,19 @@ typedef struct { static etp_reqq req_queue; static etp_reqq res_queue; -static int reqq_push (etp_reqq *q, ETP_REQ *req) +static void ecb_noinline ecb_cold +reqq_init (etp_reqq *q) +{ + int pri; + + for (pri = 0; pri < ETP_NUM_PRI; ++pri) + q->qs[pri] = q->qe[pri] = 0; + + q->size = 0; +} + +static int ecb_noinline +reqq_push (etp_reqq *q, ETP_REQ *req) { int pri = req->pri; req->next = 0; @@ -377,7 +543,8 @@ static int reqq_push (etp_reqq *q, ETP_REQ *req) return q->size++; } -static ETP_REQ *reqq_shift (etp_reqq *q) +static ETP_REQ * ecb_noinline +reqq_shift (etp_reqq *q) { int pri; @@ -402,46 +569,19 @@ static ETP_REQ *reqq_shift (etp_reqq *q) abort (); } -static void etp_atfork_prepare (void) +static int ecb_cold +etp_init (void (*want_poll)(void), void (*done_poll)(void)) { - X_LOCK (wrklock); - X_LOCK (reqlock); - X_LOCK (reslock); -#if !HAVE_PREADWRITE - X_LOCK (preadwritelock); -#endif -} + X_MUTEX_CREATE (wrklock); + X_MUTEX_CREATE (reslock); + X_MUTEX_CREATE (reqlock); + X_COND_CREATE (reqwait); -static void etp_atfork_parent (void) -{ -#if !HAVE_PREADWRITE - X_UNLOCK (preadwritelock); -#endif - X_UNLOCK (reslock); - X_UNLOCK (reqlock); - X_UNLOCK (wrklock); -} + reqq_init (&req_queue); + reqq_init (&res_queue); -static void etp_atfork_child (void) -{ - ETP_REQ *prv; - - while ((prv = reqq_shift (&req_queue))) - ETP_DESTROY (prv); - - while ((prv = reqq_shift (&res_queue))) - ETP_DESTROY (prv); - - while (wrk_first.next != &wrk_first) - { - etp_worker *wrk = wrk_first.next; - - if (wrk->req) - ETP_DESTROY (wrk->req); - - etp_worker_clear (wrk); - etp_worker_free (wrk); - } + wrk_first.next = + wrk_first.prev = &wrk_first; started = 0; idle = 0; @@ -449,22 +589,6 @@ static void etp_atfork_child (void) nready = 0; npending = 0; - etp_atfork_parent (); -} - -static void -etp_once_init (void) -{ - X_THREAD_ATFORK (etp_atfork_prepare, etp_atfork_parent, etp_atfork_child); -} - -static int -etp_init (void (*want_poll)(void), void (*done_poll)(void)) -{ - static pthread_once_t doinit = PTHREAD_ONCE_INIT; - - pthread_once (&doinit, etp_once_init); - want_poll_cb = want_poll; done_poll_cb = done_poll; @@ -473,7 +597,8 @@ etp_init (void (*want_poll)(void), void (*done_poll)(void)) X_THREAD_PROC (etp_proc); -static void etp_start_thread (void) +static void ecb_cold +etp_start_thread (void) { etp_worker *wrk = calloc (1, sizeof (etp_worker)); @@ -496,19 +621,21 @@ static void etp_start_thread (void) X_UNLOCK (wrklock); } -static void etp_maybe_start_thread (void) +static void +etp_maybe_start_thread (void) { - if (expect_true (etp_nthreads () >= wanted)) + if (ecb_expect_true (etp_nthreads () >= wanted)) return; /* todo: maybe use idle here, but might be less exact */ - if (expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ())) + if (ecb_expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ())) return; etp_start_thread (); } -static void etp_end_thread (void) +static void ecb_cold +etp_end_thread (void) { eio_req *req = calloc (1, sizeof (eio_req)); @@ -525,7 +652,8 @@ static void etp_end_thread (void) X_UNLOCK (wrklock); } -static int etp_poll (void) +static int +etp_poll (void) { unsigned int maxreqs; unsigned int maxtime; @@ -565,7 +693,7 @@ static int etp_poll (void) --nreqs; X_UNLOCK (reqlock); - if (expect_false (req->type == EIO_GROUP && req->size)) + if (ecb_expect_false (req->type == EIO_GROUP && req->size)) { req->int1 = 1; /* mark request as delayed */ continue; @@ -573,11 +701,11 @@ static int etp_poll (void) else { int res = ETP_FINISH (req); - if (expect_false (res)) + if (ecb_expect_false (res)) return res; } - if (expect_false (maxreqs && !--maxreqs)) + if (ecb_expect_false (maxreqs && !--maxreqs)) break; if (maxtime) @@ -593,23 +721,23 @@ static int etp_poll (void) return -1; } -static void etp_cancel (ETP_REQ *req) +static void +etp_cancel (ETP_REQ *req) { - X_LOCK (wrklock); - req->flags |= EIO_FLAG_CANCELLED; - X_UNLOCK (wrklock); + req->cancelled = 1; eio_grp_cancel (req); } -static void etp_submit (ETP_REQ *req) +static void +etp_submit (ETP_REQ *req) { req->pri -= ETP_PRI_MIN; - if (expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN; - if (expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN; + if (ecb_expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN; + if (ecb_expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN; - if (expect_false (req->type == EIO_GROUP)) + if (ecb_expect_false (req->type == EIO_GROUP)) { /* I hope this is worth it :/ */ X_LOCK (reqlock); @@ -638,34 +766,47 @@ static void etp_submit (ETP_REQ *req) } } -static void etp_set_max_poll_time (double nseconds) +static void ecb_cold +etp_set_max_poll_time (double nseconds) { if (WORDACCESS_UNSAFE) X_LOCK (reslock); max_poll_time = nseconds * EIO_TICKS; if (WORDACCESS_UNSAFE) X_UNLOCK (reslock); } -static void etp_set_max_poll_reqs (unsigned int maxreqs) +static void ecb_cold +etp_set_max_poll_reqs (unsigned int maxreqs) { if (WORDACCESS_UNSAFE) X_LOCK (reslock); max_poll_reqs = maxreqs; if (WORDACCESS_UNSAFE) X_UNLOCK (reslock); } -static void etp_set_max_idle (unsigned int nthreads) +static void ecb_cold +etp_set_max_idle (unsigned int nthreads) { if (WORDACCESS_UNSAFE) X_LOCK (reqlock); - max_idle = nthreads <= 0 ? 1 : nthreads; + max_idle = nthreads; if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); } -static void etp_set_min_parallel (unsigned int nthreads) +static void ecb_cold +etp_set_idle_timeout (unsigned int seconds) +{ + if (WORDACCESS_UNSAFE) X_LOCK (reqlock); + idle_timeout = seconds; + if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); +} + +static void ecb_cold +etp_set_min_parallel (unsigned int nthreads) { if (wanted < nthreads) wanted = nthreads; } -static void etp_set_max_parallel (unsigned int nthreads) +static void ecb_cold +etp_set_max_parallel (unsigned int nthreads) { if (wanted > nthreads) wanted = nthreads; @@ -676,7 +817,8 @@ static void etp_set_max_parallel (unsigned int nthreads) /*****************************************************************************/ -static void grp_try_feed (eio_req *grp) +static void +grp_try_feed (eio_req *grp) { while (grp->size < grp->int2 && !EIO_CANCELLED (grp)) { @@ -693,7 +835,8 @@ static void grp_try_feed (eio_req *grp) } } -static int grp_dec (eio_req *grp) +static int +grp_dec (eio_req *grp) { --grp->size; @@ -707,7 +850,8 @@ static int grp_dec (eio_req *grp) return 0; } -void eio_destroy (eio_req *req) +static void +eio_destroy (eio_req *req) { if ((req)->flags & EIO_FLAG_PTR1_FREE) free (req->ptr1); if ((req)->flags & EIO_FLAG_PTR2_FREE) free (req->ptr2); @@ -715,7 +859,8 @@ void eio_destroy (eio_req *req) EIO_DESTROY (req); } -static int eio_finish (eio_req *req) +static int +eio_finish (eio_req *req) { int res = EIO_FINISH (req); @@ -733,7 +878,7 @@ static int eio_finish (eio_req *req) res2 = grp_dec (grp); - if (!res && res2) + if (!res) res = res2; } @@ -742,63 +887,81 @@ static int eio_finish (eio_req *req) return res; } -void eio_grp_cancel (eio_req *grp) +void +eio_grp_cancel (eio_req *grp) { for (grp = grp->grp_first; grp; grp = grp->grp_next) eio_cancel (grp); } -void eio_cancel (eio_req *req) +void +eio_cancel (eio_req *req) { etp_cancel (req); } -void eio_submit (eio_req *req) +void +eio_submit (eio_req *req) { etp_submit (req); } -unsigned int eio_nreqs (void) +unsigned int +eio_nreqs (void) { return etp_nreqs (); } -unsigned int eio_nready (void) +unsigned int +eio_nready (void) { return etp_nready (); } -unsigned int eio_npending (void) +unsigned int +eio_npending (void) { return etp_npending (); } -unsigned int eio_nthreads (void) +unsigned int ecb_cold +eio_nthreads (void) { return etp_nthreads (); } -void eio_set_max_poll_time (double nseconds) +void ecb_cold +eio_set_max_poll_time (double nseconds) { etp_set_max_poll_time (nseconds); } -void eio_set_max_poll_reqs (unsigned int maxreqs) +void ecb_cold +eio_set_max_poll_reqs (unsigned int maxreqs) { etp_set_max_poll_reqs (maxreqs); } -void eio_set_max_idle (unsigned int nthreads) +void ecb_cold +eio_set_max_idle (unsigned int nthreads) { etp_set_max_idle (nthreads); } -void eio_set_min_parallel (unsigned int nthreads) +void ecb_cold +eio_set_idle_timeout (unsigned int seconds) +{ + etp_set_idle_timeout (seconds); +} + +void ecb_cold +eio_set_min_parallel (unsigned int nthreads) { etp_set_min_parallel (nthreads); } -void eio_set_max_parallel (unsigned int nthreads) +void ecb_cold +eio_set_max_parallel (unsigned int nthreads) { etp_set_max_parallel (nthreads); } @@ -817,10 +980,10 @@ int eio_poll (void) # define pread eio__pread # define pwrite eio__pwrite -ssize_t +eio_ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset) { - ssize_t res; + eio_ssize_t res; off_t ooffset; X_LOCK (preadwritelock); @@ -833,10 +996,10 @@ eio__pread (int fd, void *buf, size_t count, off_t offset) return res; } -ssize_t +eio_ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset) { - ssize_t res; + eio_ssize_t res; off_t ooffset; X_LOCK (preadwritelock); @@ -878,7 +1041,8 @@ eio__utimes (const char *filename, const struct timeval times[2]) # undef futimes # define futimes(fd,times) eio__futimes (fd, times) -static int eio__futimes (int fd, const struct timeval tv[2]) +static int +eio__futimes (int fd, const struct timeval tv[2]) { errno = ENOSYS; return -1; @@ -886,25 +1050,31 @@ static int eio__futimes (int fd, const struct timeval tv[2]) #endif -#ifdef _WIN32 -# define fsync(fd) (FlushFileBuffers((HANDLE)_get_osfhandle(fd)) ? 0 : -1) -#endif - #if !HAVE_FDATASYNC # undef fdatasync # define fdatasync(fd) fsync (fd) #endif -// Use unicode and big file aware stat on windows -#ifdef _WIN32 -# undef stat -# undef fstat -# define stat _stati64 -# define fstat _fstati64 +static int +eio__syncfs (int fd) +{ + int res; + +#if HAVE_SYS_SYNCFS + res = (int)syscall (__NR_syncfs, (int)(fd)); +#else + res = -1; + errno = ENOSYS; #endif + if (res < 0 && errno == ENOSYS && fd >= 0) + sync (); + + return res; +} + /* sync_file_range always needs emulation */ -int +static int eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags) { #if HAVE_SYNC_FILE_RANGE @@ -931,11 +1101,22 @@ eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags) return fdatasync (fd); } +static int +eio__fallocate (int fd, int mode, off_t offset, size_t len) +{ +#if HAVE_FALLOCATE + return fallocate (fd, mode, offset, len); +#else + errno = ENOSYS; + return -1; +#endif +} + #if !HAVE_READAHEAD # undef readahead # define readahead(fd,offset,count) eio__readahead (fd, offset, count, self) -static ssize_t +static eio_ssize_t eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) { size_t todo = count; @@ -950,6 +1131,8 @@ eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) todo -= len; } + FUBd; + errno = 0; return count; } @@ -957,93 +1140,118 @@ eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) #endif /* sendfile always needs emulation */ -static ssize_t -eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) +static eio_ssize_t +eio__sendfile (int ofd, int ifd, off_t offset, size_t count) { - ssize_t res; + eio_ssize_t written = 0; + eio_ssize_t res; if (!count) return 0; + for (;;) + { +#ifdef __APPLE__ +# undef HAVE_SENDFILE /* broken, as everything on os x */ +#endif #if HAVE_SENDFILE # if __linux - res = sendfile (ofd, ifd, &offset, count); + off_t soffset = offset; + res = sendfile (ofd, ifd, &soffset, count); # elif __FreeBSD__ - /* - * Of course, the freebsd sendfile is a dire hack with no thoughts - * wasted on making it similar to other I/O functions. - */ - { - off_t sbytes; - res = sendfile (ifd, ofd, offset, count, 0, &sbytes, 0); + /* + * Of course, the freebsd sendfile is a dire hack with no thoughts + * wasted on making it similar to other I/O functions. + */ + off_t sbytes; + res = sendfile (ifd, ofd, offset, count, 0, &sbytes, 0); - #if 0 /* according to the manpage, this is correct, but broken behaviour */ - /* freebsd' sendfile will return 0 on success */ - /* freebsd 8 documents it as only setting *sbytes on EINTR and EAGAIN, but */ - /* not on e.g. EIO or EPIPE - sounds broken */ - if ((res < 0 && (errno == EAGAIN || errno == EINTR) && sbytes) || res == 0) - res = sbytes; - #endif + #if 0 /* according to the manpage, this is correct, but broken behaviour */ + /* freebsd' sendfile will return 0 on success */ + /* freebsd 8 documents it as only setting *sbytes on EINTR and EAGAIN, but */ + /* not on e.g. EIO or EPIPE - sounds broken */ + if ((res < 0 && (errno == EAGAIN || errno == EINTR) && sbytes) || res == 0) + res = sbytes; + #endif - /* according to source inspection, this is correct, and useful behaviour */ - if (sbytes) - res = sbytes; - } + /* according to source inspection, this is correct, and useful behaviour */ + if (sbytes) + res = sbytes; # elif defined (__APPLE__) + off_t sbytes = count; + res = sendfile (ifd, ofd, offset, &sbytes, 0, 0); - { - off_t sbytes = count; - res = sendfile (ifd, ofd, offset, &sbytes, 0, 0); - - /* according to the manpage, sbytes is always valid */ - if (sbytes) - res = sbytes; - } + /* according to the manpage, sbytes is always valid */ + if (sbytes) + res = sbytes; # elif __hpux - res = sendfile (ofd, ifd, offset, count, 0, 0); + res = sendfile (ofd, ifd, offset, count, 0, 0); # elif __solaris - { - struct sendfilevec vec; - size_t sbytes; + struct sendfilevec vec; + size_t sbytes; - vec.sfv_fd = ifd; - vec.sfv_flag = 0; - vec.sfv_off = offset; - vec.sfv_len = count; + vec.sfv_fd = ifd; + vec.sfv_flag = 0; + vec.sfv_off = offset; + vec.sfv_len = count; - res = sendfilev (ofd, &vec, 1, &sbytes); + res = sendfilev (ofd, &vec, 1, &sbytes); - if (res < 0 && sbytes) - res = sbytes; - } + if (res < 0 && sbytes) + res = sbytes; # endif -//#elif defined (_WIN32) -// -// /* does not work, just for documentation of what would need to be done */ -// { -// HANDLE h = TO_SOCKET (ifd); -// SetFilePointer (h, offset, 0, FILE_BEGIN); -// res = TransmitFile (TO_SOCKET (ofd), h, count, 0, 0, 0, 0); -// } +#elif defined (_WIN32) && 0 + /* does not work, just for documentation of what would need to be done */ + /* actually, cannot be done like this, as TransmitFile changes the file offset, */ + /* libeio guarantees that the file offset does not change, and windows */ + /* has no way to get an independent handle to the same file description */ + HANDLE h = TO_SOCKET (ifd); + SetFilePointer (h, offset, 0, FILE_BEGIN); + res = TransmitFile (TO_SOCKET (ofd), h, count, 0, 0, 0, 0); #else - res = -1; - errno = ENOSYS; + res = -1; + errno = ENOSYS; #endif - if (res < 0 + /* we assume sendfile can copy at least 128mb in one go */ + if (res <= 128 * 1024 * 1024) + { + if (res > 0) + written += res; + + if (written) + return written; + + break; + } + else + { + /* if we requested more, then probably the kernel was lazy */ + written += res; + offset += res; + count -= res; + + if (!count) + return written; + } + } + + if (res < 0 && (errno == ENOSYS || errno == EINVAL || errno == ENOTSOCK /* BSDs */ #ifdef ENOTSUP /* sigh, if the steenking pile called openbsd would only try to at least compile posix code... */ || errno == ENOTSUP #endif +#ifdef EOPNOTSUPP /* windows */ || errno == EOPNOTSUPP /* BSDs */ +#endif #if __solaris || errno == EAFNOSUPPORT || errno == EPROTOTYPE #endif @@ -1057,7 +1265,7 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) while (count) { - ssize_t cnt; + eio_ssize_t cnt; cnt = pread (ifd, eio_buf, count > EIO_BUFSIZE ? EIO_BUFSIZE : count, offset); @@ -1079,16 +1287,299 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) res += cnt; count -= cnt; } + + FUBd; } return res; } +#ifdef PAGESIZE +# define eio_pagesize() PAGESIZE +#else +static intptr_t +eio_pagesize (void) +{ + static intptr_t page; + + if (!page) + page = sysconf (_SC_PAGESIZE); + + return page; +} +#endif + +static void +eio_page_align (void **addr, size_t *length) +{ + intptr_t mask = eio_pagesize () - 1; + + /* round down addr */ + intptr_t adj = mask & (intptr_t)*addr; + + *addr = (void *)((intptr_t)*addr - adj); + *length += adj; + + /* round up length */ + *length = (*length + mask) & ~mask; +} + +#if !_POSIX_MEMLOCK +# define eio__mlockall(a) EIO_ENOSYS () +#else + +static int +eio__mlockall (int flags) +{ + #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 7 + extern int mallopt (int, int); + mallopt (-6, 238); /* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=473812 */ + #endif + + if (EIO_MCL_CURRENT != MCL_CURRENT + || EIO_MCL_FUTURE != MCL_FUTURE) + { + flags = 0 + | (flags & EIO_MCL_CURRENT ? MCL_CURRENT : 0) + | (flags & EIO_MCL_FUTURE ? MCL_FUTURE : 0); + } + + return mlockall (flags); +} +#endif + +#if !_POSIX_MEMLOCK_RANGE +# define eio__mlock(a,b) EIO_ENOSYS () +#else + +static int +eio__mlock (void *addr, size_t length) +{ + eio_page_align (&addr, &length); + + return mlock (addr, length); +} + +#endif + +#if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO) +# define eio__msync(a,b,c) EIO_ENOSYS () +#else + +static int +eio__msync (void *mem, size_t len, int flags) +{ + eio_page_align (&mem, &len); + + if (EIO_MS_ASYNC != MS_SYNC + || EIO_MS_INVALIDATE != MS_INVALIDATE + || EIO_MS_SYNC != MS_SYNC) + { + flags = 0 + | (flags & EIO_MS_ASYNC ? MS_ASYNC : 0) + | (flags & EIO_MS_INVALIDATE ? MS_INVALIDATE : 0) + | (flags & EIO_MS_SYNC ? MS_SYNC : 0); + } + + return msync (mem, len, flags); +} + +#endif + +static int +eio__mtouch (eio_req *req) +{ + void *mem = req->ptr2; + size_t len = req->size; + int flags = req->int1; + + eio_page_align (&mem, &len); + + { + intptr_t addr = (intptr_t)mem; + intptr_t end = addr + len; + intptr_t page = eio_pagesize (); + + if (addr < end) + if (flags & EIO_MT_MODIFY) /* modify */ + do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len && !EIO_CANCELLED (req)); + else + do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len && !EIO_CANCELLED (req)); + } + + return 0; +} + +/*****************************************************************************/ +/* requests implemented outside eio_execute, because they are so large */ + +static void +eio__realpath (eio_req *req, etp_worker *self) +{ + char *rel = req->ptr1; + char *res; + char *tmp1, *tmp2; +#if SYMLOOP_MAX > 32 + int symlinks = SYMLOOP_MAX; +#else + int symlinks = 32; +#endif + + req->result = -1; + + errno = EINVAL; + if (!rel) + return; + + errno = ENOENT; + if (!*rel) + return; + + if (!req->ptr2) + { + X_LOCK (wrklock); + req->flags |= EIO_FLAG_PTR2_FREE; + X_UNLOCK (wrklock); + req->ptr2 = malloc (PATH_MAX * 3); + + errno = ENOMEM; + if (!req->ptr2) + return; + } + + res = req->ptr2; + tmp1 = res + PATH_MAX; + tmp2 = tmp1 + PATH_MAX; + +#if 0 /* disabled, the musl way to do things is just too racy */ +#if __linux && defined(O_NONBLOCK) && defined(O_NOATIME) + /* on linux we may be able to ask the kernel */ + { + int fd = open (rel, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_NOATIME); + + if (fd >= 0) + { + sprintf (tmp1, "/proc/self/fd/%d", fd); + req->result = readlink (tmp1, res, PATH_MAX); + close (fd); + + /* here we should probably stat the open file and the disk file, to make sure they still match */ + + if (req->result > 0) + goto done; + } + else if (errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EIO) + return; + } +#endif +#endif + + if (*rel != '/') + { + if (!getcwd (res, PATH_MAX)) + return; + + if (res [1]) /* only use if not / */ + res += strlen (res); + } + + while (*rel) + { + eio_ssize_t len, linklen; + char *beg = rel; + + while (*rel && *rel != '/') + ++rel; + + len = rel - beg; + + if (!len) /* skip slashes */ + { + ++rel; + continue; + } + + if (beg [0] == '.') + { + if (len == 1) + continue; /* . - nop */ + + if (beg [1] == '.' && len == 2) + { + /* .. - back up one component, if possible */ + + while (res != req->ptr2) + if (*--res == '/') + break; + + continue; + } + } + + errno = ENAMETOOLONG; + if (res + 1 + len + 1 >= tmp1) + return; + + /* copy one component */ + *res = '/'; + memcpy (res + 1, beg, len); + + /* zero-terminate, for readlink */ + res [len + 1] = 0; + + /* now check if it's a symlink */ + linklen = readlink (req->ptr2, tmp1, PATH_MAX); + + if (linklen < 0) + { + if (errno != EINVAL) + return; + + /* it's a normal directory. hopefully */ + res += len + 1; + } + else + { + /* yay, it was a symlink - build new path in tmp2 */ + int rellen = strlen (rel); + + errno = ENAMETOOLONG; + if (linklen + 1 + rellen >= PATH_MAX) + return; + + errno = ELOOP; + if (!--symlinks) + return; + + if (*tmp1 == '/') + res = req->ptr2; /* symlink resolves to an absolute path */ + + /* we need to be careful, as rel might point into tmp2 already */ + memmove (tmp2 + linklen + 1, rel, rellen + 1); + tmp2 [linklen] = '/'; + memcpy (tmp2, tmp1, linklen); + + rel = tmp2; + } + } + + /* special case for the lone root path */ + if (res == req->ptr2) + *res++ = '/'; + + req->result = res - (char *)req->ptr2; + +done: + req->ptr2 = realloc (req->ptr2, req->result); /* trade time for space savings */ +} + static signed char eio_dent_cmp (const eio_dirent *a, const eio_dirent *b) { - return a->score - b->score ? a->score - b->score /* works because our signed char is always 0..100 */ - : a->inode < b->inode ? -1 : a->inode > b->inode ? 1 : 0; + return a->score - b->score ? a->score - b->score /* works because our signed char is always 0..100 */ + : a->inode < b->inode ? -1 + : a->inode > b->inode ? 1 + : 0; } #define EIO_DENT_CMP(i,op,j) eio_dent_cmp (&i, &j) op 0 @@ -1097,41 +1588,41 @@ eio_dent_cmp (const eio_dirent *a, const eio_dirent *b) #define EIO_SORT_FAST 60 /* when to only use insertion sort */ static void -eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits) +eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) { - unsigned char bits [9 + sizeof (ino_t) * 8]; + unsigned char bits [9 + sizeof (eio_ino_t) * 8]; unsigned char *bit = bits; assert (CHAR_BIT == 8); assert (sizeof (eio_dirent) * 8 < 256); - assert (offsetof (eio_dirent, inode)); /* we use 0 as sentinel */ - assert (offsetof (eio_dirent, score)); /* we use 0 as sentinel */ + assert (offsetof (eio_dirent, inode)); /* we use bit #0 as sentinel */ + assert (offsetof (eio_dirent, score)); /* we use bit #0 as sentinel */ if (size <= EIO_SORT_FAST) return; /* first prepare an array of bits to test in our radix sort */ - /* try to take endianness into account, as well as differences in ino_t sizes */ + /* try to take endianness into account, as well as differences in eio_ino_t sizes */ /* inode_bits must contain all inodes ORed together */ /* which is used to skip bits that are 0 everywhere, which is very common */ { - ino_t endianness; + eio_ino_t endianness; int i, j; /* we store the byte offset of byte n into byte n of "endianness" */ - for (i = 0; i < sizeof (ino_t); ++i) + for (i = 0; i < sizeof (eio_ino_t); ++i) ((unsigned char *)&endianness)[i] = i; *bit++ = 0; - for (i = 0; i < sizeof (ino_t); ++i) + for (i = 0; i < sizeof (eio_ino_t); ++i) { /* shifting off the byte offsets out of "endianness" */ int offs = (offsetof (eio_dirent, inode) + (endianness & 0xff)) * 8; endianness >>= 8; for (j = 0; j < 8; ++j) - if (inode_bits & (((ino_t)1) << (i * 8 + j))) + if (inode_bits & (((eio_ino_t)1) << (i * 8 + j))) *bit++ = offs + j; } @@ -1142,9 +1633,9 @@ eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, ino_t /* now actually do the sorting (a variant of MSD radix sort) */ { - eio_dirent *base_stk [9 + sizeof (ino_t) * 8], *base; - eio_dirent *end_stk [9 + sizeof (ino_t) * 8], *end; - unsigned char *bit_stk [9 + sizeof (ino_t) * 8]; + eio_dirent *base_stk [9 + sizeof (eio_ino_t) * 8], *base; + eio_dirent *end_stk [9 + sizeof (eio_ino_t) * 8], *end; + unsigned char *bit_stk [9 + sizeof (eio_ino_t) * 8]; int stk_idx = 0; base_stk [stk_idx] = dents; @@ -1233,7 +1724,7 @@ eio_dent_insertion_sort (eio_dirent *dents, int size) } static void -eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits) +eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) { if (size <= 1) return; /* our insertion sort relies on size > 0 */ @@ -1251,47 +1742,112 @@ eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_ static void eio__scandir (eio_req *req, etp_worker *self) { - DIR *dirp; - EIO_STRUCT_DIRENT *entp; char *name, *names; - int namesalloc = 4096; + int namesalloc = 4096 - sizeof (void *) * 4; int namesoffs = 0; int flags = req->int1; eio_dirent *dents = 0; int dentalloc = 128; int dentoffs = 0; - ino_t inode_bits = 0; + eio_ino_t inode_bits = 0; +#ifdef _WIN32 + HANDLE dirp; + WIN32_FIND_DATA entp; +#else + DIR *dirp; + EIO_STRUCT_DIRENT *entp; +#endif req->result = -1; if (!(flags & EIO_READDIR_DENTS)) flags &= ~(EIO_READDIR_DIRS_FIRST | EIO_READDIR_STAT_ORDER); - X_LOCK (wrklock); - /* the corresponding closedir is in ETP_WORKER_CLEAR */ - self->dirp = dirp = opendir (req->ptr1); +#ifdef _WIN32 + { + int len = strlen ((const char *)req->ptr1); + char *path = malloc (MAX_PATH); + const char *fmt; + + if (!len) + fmt = "./*"; + else if (((const char *)req->ptr1)[len - 1] == '/' || ((const char *)req->ptr1)[len - 1] == '\\') + fmt = "%s*"; + else + fmt = "%s/*"; + + _snprintf (path, MAX_PATH, fmt, (const char *)req->ptr1); + dirp = FindFirstFile (path, &entp); + free (path); + + if (dirp == INVALID_HANDLE_VALUE) + { + dirp = 0; + + /* should steal _dosmaperr */ + switch (GetLastError ()) + { + case ERROR_FILE_NOT_FOUND: + req->result = 0; + break; + + case ERROR_INVALID_NAME: + case ERROR_PATH_NOT_FOUND: + case ERROR_NO_MORE_FILES: + errno = ENOENT; + break; + + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + + default: + errno = EINVAL; + break; + } + } + } +#else + dirp = opendir (req->ptr1); +#endif + + if (req->flags & EIO_FLAG_PTR1_FREE) + free (req->ptr1); + req->flags |= EIO_FLAG_PTR1_FREE | EIO_FLAG_PTR2_FREE; req->ptr1 = dents = flags ? malloc (dentalloc * sizeof (eio_dirent)) : 0; req->ptr2 = names = malloc (namesalloc); - X_UNLOCK (wrklock); if (dirp && names && (!flags || dents)) for (;;) { + int done; + +#ifdef _WIN32 + done = !dirp; +#else errno = 0; entp = readdir (dirp); + done = !entp; +#endif - if (!entp) + if (done) { +#ifndef _WIN32 + int old_errno = errno; + closedir (dirp); + errno = old_errno; + if (errno) break; +#endif /* sort etc. */ req->int1 = flags; req->result = dentoffs; if (flags & EIO_READDIR_STAT_ORDER) - eio_dent_sort (dents, dentoffs, 0, inode_bits); /* sort by inode exclusively */ + eio_dent_sort (dents, dentoffs, flags & EIO_READDIR_DIRS_FIRST ? 7 : 0, inode_bits); else if (flags & EIO_READDIR_DIRS_FIRST) if (flags & EIO_READDIR_FOUND_UNKNOWN) eio_dent_sort (dents, dentoffs, 7, inode_bits); /* sort by score and inode */ @@ -1303,7 +1859,6 @@ eio__scandir (eio_req *req, etp_worker *self) /* now partition dirs to the front, and non-dirs to the back */ /* by walking from both sides and swapping if necessary */ - /* also clear score, so it doesn't influence sorting */ while (oth > dir) { if (dir->type == EIO_DT_DIR) @@ -1316,7 +1871,7 @@ eio__scandir (eio_req *req, etp_worker *self) } } - /* now sort the dirs only */ + /* now sort the dirs only (dirs all have the same score) */ eio_dent_sort (dents, dir - dents, 0, inode_bits); } @@ -1324,19 +1879,17 @@ eio__scandir (eio_req *req, etp_worker *self) } /* now add the entry to our list(s) */ - name = entp->d_name; + name = D_NAME (entp); /* skip . and .. entries */ if (name [0] != '.' || (name [1] && (name [1] != '.' || name [2]))) { int len = D_NAMLEN (entp) + 1; - while (expect_false (namesoffs + len > namesalloc)) + while (ecb_expect_false (namesoffs + len > namesalloc)) { namesalloc *= 2; - X_LOCK (wrklock); req->ptr2 = names = realloc (names, namesalloc); - X_UNLOCK (wrklock); if (!names) break; @@ -1348,12 +1901,10 @@ eio__scandir (eio_req *req, etp_worker *self) { struct eio_dirent *ent; - if (expect_false (dentoffs == dentalloc)) + if (ecb_expect_false (dentoffs == dentalloc)) { dentalloc *= 2; - X_LOCK (wrklock); req->ptr1 = dents = realloc (dents, dentalloc * sizeof (eio_dirent)); - X_UNLOCK (wrklock); if (!dents) break; @@ -1443,133 +1994,17 @@ eio__scandir (eio_req *req, etp_worker *self) errno = ECANCELED; break; } + +#ifdef _WIN32 + if (!FindNextFile (dirp, &entp)) + { + FindClose (dirp); + dirp = 0; + } +#endif } } -#ifdef PAGESIZE -# define eio_pagesize() PAGESIZE - -#elif defined(_WIN32) - /* Windows */ - static intptr_t - eio_pagesize (void) - { - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwPageSize; - } - -#else - /* POSIX */ - static intptr_t - eio_pagesize (void) - { - static intptr_t page; - - if (!page) - page = sysconf (_SC_PAGESIZE); - - return page; - } -#endif - -static void -eio_page_align (void **addr, size_t *length) -{ - intptr_t mask = eio_pagesize () - 1; - - /* round down addr */ - intptr_t adj = mask & (intptr_t)*addr; - - *addr = (void *)((intptr_t)*addr - adj); - *length += adj; - - /* round up length */ - *length = (*length + mask) & ~mask; -} - -#if !_POSIX_MEMLOCK -# define eio__mlockall(a) ((errno = ENOSYS), -1) -#else - -static int -eio__mlockall (int flags) -{ - #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 7 - extern int mallopt (int, int); - mallopt (-6, 238); /* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=473812 */ - #endif - - if (EIO_MCL_CURRENT != MCL_CURRENT - || EIO_MCL_FUTURE != MCL_FUTURE) - { - flags = 0 - | (flags & EIO_MCL_CURRENT ? MCL_CURRENT : 0) - | (flags & EIO_MCL_FUTURE ? MCL_FUTURE : 0); - } - - return mlockall (flags); -} -#endif - -#if !_POSIX_MEMLOCK_RANGE -# define eio__mlock(a,b) ((errno = ENOSYS), -1) -#else - -static int -eio__mlock (void *addr, size_t length) -{ - eio_page_align (&addr, &length); - - return mlock (addr, length); -} - -#endif - -#if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO) -# define eio__msync(a,b,c) ((errno = ENOSYS), -1) -#else - -int -eio__msync (void *mem, size_t len, int flags) -{ - eio_page_align (&mem, &len); - - if (EIO_MS_ASYNC != MS_SYNC - || EIO_MS_INVALIDATE != MS_INVALIDATE - || EIO_MS_SYNC != MS_SYNC) - { - flags = 0 - | (flags & EIO_MS_ASYNC ? MS_ASYNC : 0) - | (flags & EIO_MS_INVALIDATE ? MS_INVALIDATE : 0) - | (flags & EIO_MS_SYNC ? MS_SYNC : 0); - } - - return msync (mem, len, flags); -} - -#endif - -int -eio__mtouch (void *mem, size_t len, int flags) -{ - eio_page_align (&mem, &len); - - { - intptr_t addr = (intptr_t)mem; - intptr_t end = addr + len; - intptr_t page = eio_pagesize (); - - if (addr < end) - if (flags & EIO_MT_MODIFY) /* modify */ - do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len); - else - do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len); - } - - return 0; -} - /*****************************************************************************/ #define ALLOC(len) \ @@ -1593,11 +2028,17 @@ X_THREAD_PROC (etp_proc) struct timespec ts; etp_worker *self = (etp_worker *)thr_arg; - /* try to distribute timeouts somewhat randomly */ +#if HAVE_PRCTL_SET_NAME + prctl (PR_SET_NAME, (unsigned long)"eio_thread", 0, 0, 0); +#endif + + /* try to distribute timeouts somewhat evenly */ ts.tv_nsec = ((unsigned long)self & 1023UL) * (1000000000UL / 1024UL); for (;;) { + ts.tv_sec = 0; + X_LOCK (reqlock); for (;;) @@ -1607,23 +2048,28 @@ X_THREAD_PROC (etp_proc) if (req) break; + if (ts.tv_sec == 1) /* no request, but timeout detected, let's quit */ + { + X_UNLOCK (reqlock); + X_LOCK (wrklock); + --started; + X_UNLOCK (wrklock); + goto quit; + } + ++idle; - ts.tv_sec = time (0) + IDLE_TIMEOUT; - if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) + if (idle <= max_idle) + /* we are allowed to idle, so do so without any timeout */ + X_COND_WAIT (reqwait, reqlock); + else { - if (idle > max_idle) - { - --idle; - X_UNLOCK (reqlock); - X_LOCK (wrklock); - --started; - X_UNLOCK (wrklock); - goto quit; - } + /* initialise timeout once */ + if (!ts.tv_sec) + ts.tv_sec = time (0) + idle_timeout; - /* we are allowed to idle, so do so without any timeout */ - X_COND_WAIT (reqwait, reqlock); + if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) + ts.tv_sec = 1; /* assuming this is not a value computed above.,.. */ } --idle; @@ -1636,8 +2082,7 @@ X_THREAD_PROC (etp_proc) if (req->type < 0) goto quit; - if (!EIO_CANCELLED (req)) - ETP_EXECUTE (self, req); + ETP_EXECUTE (self, req); X_LOCK (reslock); @@ -1662,12 +2107,18 @@ quit: /*****************************************************************************/ -int eio_init (void (*want_poll)(void), void (*done_poll)(void)) +int ecb_cold +eio_init (void (*want_poll)(void), void (*done_poll)(void)) { +#if !HAVE_PREADWRITE + X_MUTEX_CREATE (preadwritelock); +#endif + return etp_init (want_poll, done_poll); } -static void eio_api_destroy (eio_req *req) +ecb_inline void +eio_api_destroy (eio_req *req) { free (req); } @@ -1696,14 +2147,23 @@ static void eio_api_destroy (eio_req *req) return 0; \ } -static void eio_execute (etp_worker *self, eio_req *req) +static void +eio_execute (etp_worker *self, eio_req *req) { + if (ecb_expect_false (EIO_CANCELLED (req))) + { + req->result = -1; + req->errorno = ECANCELED; + return; + } + switch (req->type) { case EIO_READ: ALLOC (req->size); req->result = req->offs >= 0 ? pread (req->int1, req->ptr2, req->size, req->offs) : read (req->int1, req->ptr2, req->size); break; + case EIO_WRITE: #if defined (__APPLE__) pthread_mutex_lock (&apple_bug_writelock); @@ -1719,18 +2179,15 @@ static void eio_execute (etp_worker *self, eio_req *req) break; case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break; - case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size, self); break; + case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size); break; case EIO_STAT: ALLOC (sizeof (EIO_STRUCT_STAT)); req->result = stat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break; -#ifndef _WIN32 case EIO_LSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); req->result = lstat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break; -#endif case EIO_FSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); req->result = fstat (req->int1, (EIO_STRUCT_STAT *)req->ptr2); break; -#ifndef _WIN32 case EIO_STATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); req->result = statvfs (req->ptr1, (EIO_STRUCT_STATVFS *)req->ptr2); break; case EIO_FSTATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); @@ -1738,46 +2195,37 @@ static void eio_execute (etp_worker *self, eio_req *req) case EIO_CHOWN: req->result = chown (req->ptr1, req->int2, req->int3); break; case EIO_FCHOWN: req->result = fchown (req->int1, req->int2, req->int3); break; -#endif - case EIO_CHMOD: req->result = chmod (req->ptr1, (mode_t)req->int2); break; -#ifndef _WIN32 - case EIO_FCHMOD: req->result = fchmod (req->int1, (mode_t)req->int2); break; + case EIO_CHMOD: req->result = chmod (req->ptr1, (eio_mode_t)req->int2); break; + case EIO_FCHMOD: req->result = fchmod (req->int1, (eio_mode_t)req->int2); break; case EIO_TRUNCATE: req->result = truncate (req->ptr1, req->offs); break; -#endif case EIO_FTRUNCATE: req->result = ftruncate (req->int1, req->offs); break; - case EIO_OPEN: req->result = open (req->ptr1, req->int1, (mode_t)req->int2); break; + case EIO_OPEN: req->result = open (req->ptr1, req->int1, (eio_mode_t)req->int2); break; case EIO_CLOSE: req->result = close (req->int1); break; case EIO_DUP2: req->result = dup2 (req->int1, req->int2); break; case EIO_UNLINK: req->result = unlink (req->ptr1); break; case EIO_RMDIR: req->result = rmdir (req->ptr1); break; -#ifdef _WIN32 - case EIO_MKDIR: req->result = mkdir (req->ptr1); break; -#else - case EIO_MKDIR: req->result = mkdir (req->ptr1, (mode_t)req->int2); break; -#endif + case EIO_MKDIR: req->result = mkdir (req->ptr1, (eio_mode_t)req->int2); break; case EIO_RENAME: req->result = rename (req->ptr1, req->ptr2); break; -#ifndef _WIN32 case EIO_LINK: req->result = link (req->ptr1, req->ptr2); break; case EIO_SYMLINK: req->result = symlink (req->ptr1, req->ptr2); break; - case EIO_MKNOD: req->result = mknod (req->ptr1, (mode_t)req->int2, (dev_t)req->int3); break; -#endif + case EIO_MKNOD: req->result = mknod (req->ptr1, (eio_mode_t)req->int2, (dev_t)req->offs); break; + + case EIO_REALPATH: eio__realpath (req, self); break; -#ifndef _WIN32 case EIO_READLINK: ALLOC (PATH_MAX); req->result = readlink (req->ptr1, req->ptr2, PATH_MAX); break; -#endif -#ifndef _WIN32 case EIO_SYNC: req->result = 0; sync (); break; -#endif case EIO_FSYNC: req->result = fsync (req->int1); break; case EIO_FDATASYNC: req->result = fdatasync (req->int1); break; + case EIO_SYNCFS: req->result = eio__syncfs (req->int1); break; + case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break; case EIO_MSYNC: req->result = eio__msync (req->ptr2, req->size, req->int1); break; - case EIO_MTOUCH: req->result = eio__mtouch (req->ptr2, req->size, req->int1); break; + case EIO_MTOUCH: req->result = eio__mtouch (req); break; case EIO_MLOCK: req->result = eio__mlock (req->ptr2, req->size); break; case EIO_MLOCKALL: req->result = eio__mlockall (req->int1); break; - case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break; + case EIO_FALLOCATE: req->result = eio__fallocate (req->int1, req->int2, req->offs, req->size); break; case EIO_READDIR: eio__scandir (req, self); break; @@ -1828,7 +2276,7 @@ static void eio_execute (etp_worker *self, eio_req *req) break; case EIO_CUSTOM: - ((void (*)(eio_req *))req->feed) (req); + req->feed (req); break; default: @@ -1867,6 +2315,21 @@ eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, vo REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; } +eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) +{ + REQ (EIO_FDATASYNC); req->int1 = fd; SEND; +} + +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data) +{ + REQ (EIO_SYNCFS); req->int1 = fd; SEND; +} + +eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) +{ + REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; +} + eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) { REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; @@ -1882,14 +2345,9 @@ eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data) REQ (EIO_MLOCKALL); req->int1 = flags; SEND; } -eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data) { - REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; -} - -eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) -{ - REQ (EIO_FDATASYNC); req->int1 = fd; SEND; + REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND; } eio_req *eio_close (int fd, int pri, eio_cb cb, void *data) @@ -1932,12 +2390,12 @@ eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) REQ (EIO_FTRUNCATE); req->int1 = fd; req->offs = offset; SEND; } -eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_FCHMOD); req->int1 = fd; req->int2 = (long)mode; SEND; } -eio_req *eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) { REQ (EIO_FCHOWN); req->int1 = fd; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } @@ -1952,7 +2410,7 @@ eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, in REQ (EIO_SENDFILE); req->int1 = out_fd; req->int2 = in_fd; req->offs = in_offset; req->size = length; SEND; } -eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_OPEN); PATH; req->int1 = flags; req->int2 = (long)mode; SEND; } @@ -1967,17 +2425,17 @@ eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void REQ (EIO_TRUNCATE); PATH; req->offs = offset; SEND; } -eio_req *eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) { REQ (EIO_CHOWN); PATH; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } -eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_CHMOD); PATH; req->int2 = (long)mode; SEND; } -eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_MKDIR); PATH; req->int2 = (long)mode; SEND; } @@ -1993,6 +2451,11 @@ eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data) return eio__1path (EIO_READLINK, path, pri, cb, data); } +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data) +{ + return eio__1path (EIO_REALPATH, path, pri, cb, data); +} + eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data) { return eio__1path (EIO_STAT, path, pri, cb, data); @@ -2023,9 +2486,9 @@ eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *dat REQ (EIO_READDIR); PATH; req->int1 = flags; SEND; } -eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) { - REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->int3 = (long)dev; SEND; + REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->offs = (off_t)dev; SEND; } static eio_req * @@ -2059,9 +2522,9 @@ eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, return eio__2path (EIO_RENAME, path, new_path, pri, cb, data); } -eio_req *eio_custom (eio_cb execute, int pri, eio_cb cb, void *data) +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data) { - REQ (EIO_CUSTOM); req->feed = (void (*)(eio_req *))execute; SEND; + REQ (EIO_CUSTOM); req->feed = execute; SEND; } #endif @@ -2080,7 +2543,8 @@ eio_req *eio_grp (eio_cb cb, void *data) /*****************************************************************************/ /* grp functions */ -void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit) +void +eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit) { grp->int2 = limit; grp->feed = feed; @@ -2088,14 +2552,16 @@ void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit) grp_try_feed (grp); } -void eio_grp_limit (eio_req *grp, int limit) +void +eio_grp_limit (eio_req *grp, int limit) { grp->int2 = limit; grp_try_feed (grp); } -void eio_grp_add (eio_req *grp, eio_req *req) +void +eio_grp_add (eio_req *grp, eio_req *req) { assert (("cannot add requests to IO::AIO::GRP after the group finished", grp->int1 != 2)); @@ -2116,18 +2582,9 @@ void eio_grp_add (eio_req *grp, eio_req *req) /*****************************************************************************/ /* misc garbage */ -ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count) +eio_ssize_t +eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count) { - etp_worker wrk; - ssize_t ret; - - wrk.dbuf = 0; - - ret = eio__sendfile (ofd, ifd, offset, count, &wrk); - - if (wrk.dbuf) - free (wrk.dbuf); - - return ret; + return eio__sendfile (ofd, ifd, offset, count); } diff --git a/src/rt/libuv/src/unix/eio/eio.pod b/src/rt/libuv/src/unix/eio/eio.pod new file mode 100644 index 00000000000..da5f33c01da --- /dev/null +++ b/src/rt/libuv/src/unix/eio/eio.pod @@ -0,0 +1,969 @@ +=head1 NAME + +libeio - truly asynchronous POSIX I/O + +=head1 SYNOPSIS + + #include + +=head1 DESCRIPTION + +The newest version of this document is also available as an html-formatted +web page you might find easier to navigate when reading it for the first +time: L. + +Note that this library is a by-product of the C perl +module, and many of the subtler points regarding requests lifetime +and so on are only documented in its documentation at the +moment: L. + +=head2 FEATURES + +This library provides fully asynchronous versions of most POSIX functions +dealing with I/O. Unlike most asynchronous libraries, this not only +includes C and C, but also C, C, C and +similar functions, as well as less rarely ones such as C, C +or C. + +It also offers wrappers around C (Solaris, Linux, HP-UX and +FreeBSD, with emulation on other platforms) and C (Linux, with +emulation elsewhere>). + +The goal is to enable you to write fully non-blocking programs. For +example, in a game server, you would not want to freeze for a few seconds +just because the server is running a backup and you happen to call +C. + +=head2 TIME REPRESENTATION + +Libeio represents time as a single floating point number, representing the +(fractional) number of seconds since the (POSIX) epoch (somewhere near +the beginning of 1970, details are complicated, don't ask). This type is +called C, but it is guaranteed to be of type C (or +better), so you can freely use C yourself. + +Unlike the name component C might indicate, it is also used for +time differences throughout libeio. + +=head2 FORK SUPPORT + +Usage of pthreads in a program changes the semantics of fork +considerably. Specifically, only async-safe functions can be called after +fork. Libeio uses pthreads, so this applies, and makes using fork hard for +anything but relatively fork + exec uses. + +This library only works in the process that initialised it: Forking is +fully supported, but using libeio in any other process than the one that +called C is not. + +You might get around by not I libeio before (or after) forking in +the parent, and using it in the child afterwards. You could also try to +call the L function again in the child, which will brutally +reinitialise all data structures, which isn't POSIX conformant, but +typically works. + +Otherwise, the only recommendation you should follow is: treat fork code +the same way you treat signal handlers, and only ever call C in +the process that uses it, and only once ever. + +=head1 INITIALISATION/INTEGRATION + +Before you can call any eio functions you first have to initialise the +library. The library integrates into any event loop, but can also be used +without one, including in polling mode. + +You have to provide the necessary glue yourself, however. + +=over 4 + +=item int eio_init (void (*want_poll)(void), void (*done_poll)(void)) + +This function initialises the library. On success it returns C<0>, on +failure it returns C<-1> and sets C appropriately. + +It accepts two function pointers specifying callbacks as argument, both of +which can be C<0>, in which case the callback isn't called. + +There is currently no way to change these callbacks later, or to +"uninitialise" the library again. + +=item want_poll callback + +The C callback is invoked whenever libeio wants attention (i.e. +it wants to be polled by calling C). It is "edge-triggered", +that is, it will only be called once when eio wants attention, until all +pending requests have been handled. + +This callback is called while locks are being held, so I. That includes +C. What you should do is notify some other thread, or wake up +your event loop, and then call C. + +=item done_poll callback + +This callback is invoked when libeio detects that all pending requests +have been handled. It is "edge-triggered", that is, it will only be +called once after C. To put it differently, C and +C are invoked in pairs: after C you have to call +C until either C indicates that everything has been +handled or C has been called, which signals the same. + +Note that C might return after C and C +have been called again, so watch out for races in your code. + +As with C, this callback is called while locks are being held, +so you I. + +=item int eio_poll () + +This function has to be called whenever there are pending requests that +need finishing. You usually call this after C has indicated +that you should do so, but you can also call this function regularly to +poll for new results. + +If any request invocation returns a non-zero value, then C +immediately returns with that value as return value. + +Otherwise, if all requests could be handled, it returns C<0>. If for some +reason not all requests have been handled, i.e. some are still pending, it +returns C<-1>. + +=back + +For libev, you would typically use an C watcher: the +C callback would invoke C to wake up the event +loop. Inside the callback set for the watcher, one would call C. + +If C is configured to not handle all results in one go +(i.e. it returns C<-1>) then you should start an idle watcher that calls +C until it returns something C. + +A full-featured connector between libeio and libev would look as follows +(if C is handling all requests, it can of course be simplified a +lot by removing the idle watcher logic): + + static struct ev_loop *loop; + static ev_idle repeat_watcher; + static ev_async ready_watcher; + + /* idle watcher callback, only used when eio_poll */ + /* didn't handle all results in one call */ + static void + repeat (EV_P_ ev_idle *w, int revents) + { + if (eio_poll () != -1) + ev_idle_stop (EV_A_ w); + } + + /* eio has some results, process them */ + static void + ready (EV_P_ ev_async *w, int revents) + { + if (eio_poll () == -1) + ev_idle_start (EV_A_ &repeat_watcher); + } + + /* wake up the event loop */ + static void + want_poll (void) + { + ev_async_send (loop, &ready_watcher) + } + + void + my_init_eio () + { + loop = EV_DEFAULT; + + ev_idle_init (&repeat_watcher, repeat); + ev_async_init (&ready_watcher, ready); + ev_async_start (loop &watcher); + + eio_init (want_poll, 0); + } + +For most other event loops, you would typically use a pipe - the event +loop should be told to wait for read readiness on the read end. In +C you would write a single byte, in C you would try +to read that byte, and in the callback for the read end, you would call +C. + +You don't have to take special care in the case C doesn't handle +all requests, as the done callback will not be invoked, so the event loop +will still signal readiness for the pipe until I results have been +processed. + + +=head1 HIGH LEVEL REQUEST API + +Libeio has both a high-level API, which consists of calling a request +function with a callback to be called on completion, and a low-level API +where you fill out request structures and submit them. + +This section describes the high-level API. + +=head2 REQUEST SUBMISSION AND RESULT PROCESSING + +You submit a request by calling the relevant C function with the +required parameters, a callback of type C +(called C below) and a freely usable C argument. + +The return value will either be 0, in case something went really wrong +(which can basically only happen on very fatal errors, such as C +returning 0, which is rather unlikely), or a pointer to the newly-created +and submitted C. + +The callback will be called with an C which contains the +results of the request. The members you can access inside that structure +vary from request to request, except for: + +=over 4 + +=item C + +This contains the result value from the call (usually the same as the +syscall of the same name). + +=item C + +This contains the value of C after the call. + +=item C + +The C member simply stores the value of the C argument. + +=back + +The return value of the callback is normally C<0>, which tells libeio to +continue normally. If a callback returns a nonzero value, libeio will +stop processing results (in C) and will return the value to its +caller. + +Memory areas passed to libeio must stay valid as long as a request +executes, with the exception of paths, which are being copied +internally. Any memory libeio itself allocates will be freed after the +finish callback has been called. If you want to manage all memory passed +to libeio yourself you can use the low-level API. + +For example, to open a file, you could do this: + + static int + file_open_done (eio_req *req) + { + if (req->result < 0) + { + /* open() returned -1 */ + errno = req->errorno; + perror ("open"); + } + else + { + int fd = req->result; + /* now we have the new fd in fd */ + } + + return 0; + } + + /* the first three arguments are passed to open(2) */ + /* the remaining are priority, callback and data */ + if (!eio_open ("/etc/passwd", O_RDONLY, 0, 0, file_open_done, 0)) + abort (); /* something went wrong, we will all die!!! */ + +Note that you additionally need to call C when the C +indicates that requests are ready to be processed. + +=head2 CANCELLING REQUESTS + +Sometimes the need for a request goes away before the request is +finished. In that case, one can cancel the request by a call to +C: + +=over 4 + +=item eio_cancel (eio_req *req) + +Cancel the request (and all its subrequests). If the request is currently +executing it might still continue to execute, and in other cases it might +still take a while till the request is cancelled. + +Even if cancelled, the finish callback will still be invoked - the +callbacks of all cancellable requests need to check whether the request +has been cancelled by calling C: + + static int + my_eio_cb (eio_req *req) + { + if (EIO_CANCELLED (req)) + return 0; + } + +In addition, cancelled requests will I have C<< req->result >> +set to C<-1> and C to C, or I they were +successfully executed, despite being cancelled (e.g. when they have +already been executed at the time they were cancelled). + +C is still true for requests that have successfully +executed, as long as C was called on them at some point. + +=back + +=head2 AVAILABLE REQUESTS + +The following request functions are available. I of them return the +C on success and C<0> on failure, and I of them have the +same three trailing arguments: C, C and C. The C is +mandatory, but in most cases, you pass in C<0> as C and C<0> or some +custom data value as C. + +=head3 POSIX API WRAPPERS + +These requests simply wrap the POSIX call of the same name, with the same +arguments. If a function is not implemented by the OS and cannot be emulated +in some way, then all of these return C<-1> and set C to C. + +=over 4 + +=item eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) + +=item eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) + +=item eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_rmdir (const char *path, int pri, eio_cb cb, void *data) + +=item eio_unlink (const char *path, int pri, eio_cb cb, void *data) + +=item eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) + +=item eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) + +=item eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data) + +=item eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data) + +=item eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) + +=item eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) + +=item eio_close (int fd, int pri, eio_cb cb, void *data) + +=item eio_sync (int pri, eio_cb cb, void *data) + +=item eio_fsync (int fd, int pri, eio_cb cb, void *data) + +=item eio_fdatasync (int fd, int pri, eio_cb cb, void *data) + +=item eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) + +=item eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) + +=item eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) + +=item eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) + +These have the same semantics as the syscall of the same name, their +return value is available as C<< req->result >> later. + +=item eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) + +=item eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) + +These two requests are called C and C, but actually wrap +C and C. On systems that lack these calls (such as cygwin), +libeio uses lseek/read_or_write/lseek and a mutex to serialise the +requests, so all these requests run serially and do not disturb each +other. However, they still disturb the file offset while they run, so it's +not safe to call these functions concurrently with non-libeio functions on +the same fd on these systems. + +Not surprisingly, pread and pwrite are not thread-safe on Darwin (OS/X), +so it is advised not to submit multiple requests on the same fd on this +horrible pile of garbage. + +=item eio_mlockall (int flags, int pri, eio_cb cb, void *data) + +Like C, but the flag value constants are called +C and C. + +=item eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) + +Just like msync, except that the flag values are called C, +C and C. + +=item eio_readlink (const char *path, int pri, eio_cb cb, void *data) + +If successful, the path read by C can be accessed via C<< +req->ptr2 >> and is I null-terminated, with the length specified as +C<< req->result >>. + + if (req->result >= 0) + { + char *target = strndup ((char *)req->ptr2, req->result); + + free (target); + } + +=item eio_realpath (const char *path, int pri, eio_cb cb, void *data) + +Similar to the realpath libc function, but unlike that one, C<< +req->result >> is C<-1> on failure. On success, the result is the length +of the returned path in C (which is I 0-terminated) - this is +similar to readlink. + +=item eio_stat (const char *path, int pri, eio_cb cb, void *data) + +=item eio_lstat (const char *path, int pri, eio_cb cb, void *data) + +=item eio_fstat (int fd, int pri, eio_cb cb, void *data) + +Stats a file - if C<< req->result >> indicates success, then you can +access the C-like structure via C<< req->ptr2 >>: + + EIO_STRUCT_STAT *statdata = (EIO_STRUCT_STAT *)req->ptr2; + +=item eio_statvfs (const char *path, int pri, eio_cb cb, void *data) + +=item eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) + +Stats a filesystem - if C<< req->result >> indicates success, then you can +access the C-like structure via C<< req->ptr2 >>: + + EIO_STRUCT_STATVFS *statdata = (EIO_STRUCT_STATVFS *)req->ptr2; + +=back + +=head3 READING DIRECTORIES + +Reading directories sounds simple, but can be rather demanding, especially +if you want to do stuff such as traversing a directory hierarchy or +processing all files in a directory. Libeio can assist these complex tasks +with it's C call. + +=over 4 + +=item eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data) + +This is a very complex call. It basically reads through a whole directory +(via the C, C and C calls) and returns either +the names or an array of C, depending on the C +argument. + +The C<< req->result >> indicates either the number of files found, or +C<-1> on error. On success, null-terminated names can be found as C<< req->ptr2 >>, +and C, if requested by C, can be found via C<< +req->ptr1 >>. + +Here is an example that prints all the names: + + int i; + char *names = (char *)req->ptr2; + + for (i = 0; i < req->result; ++i) + { + printf ("name #%d: %s\n", i, names); + + /* move to next name */ + names += strlen (names) + 1; + } + +Pseudo-entries such as F<.> and F<..> are never returned by C. + +C can be any combination of: + +=over 4 + +=item EIO_READDIR_DENTS + +If this flag is specified, then, in addition to the names in C, +also an array of C is returned, in C. A C looks like this: + + struct eio_dirent + { + int nameofs; /* offset of null-terminated name string in (char *)req->ptr2 */ + unsigned short namelen; /* size of filename without trailing 0 */ + unsigned char type; /* one of EIO_DT_* */ + signed char score; /* internal use */ + ino_t inode; /* the inode number, if available, otherwise unspecified */ + }; + +The only members you normally would access are C, which is the +byte-offset from C to the start of the name, C and C. + +C can be one of: + +C - if the type is not known (very common) and you have to C +the name yourself if you need to know, +one of the "standard" POSIX file types (C, C, C, +C, C, C, C) +or some OS-specific type (currently +C - multiplexed char device (v7+coherent), +C - xenix special named file, +C - multiplexed block device (v7+coherent), +C - HP-UX network special, +C - VxFS compressed, +C - solaris door, or +C). + +This example prints all names and their type: + + int i; + struct eio_dirent *ents = (struct eio_dirent *)req->ptr1; + char *names = (char *)req->ptr2; + + for (i = 0; i < req->result; ++i) + { + struct eio_dirent *ent = ents + i; + char *name = names + ent->nameofs; + + printf ("name #%d: %s (type %d)\n", i, name, ent->type); + } + +=item EIO_READDIR_DIRS_FIRST + +When this flag is specified, then the names will be returned in an order +where likely directories come first, in optimal C order. This is +useful when you need to quickly find directories, or you want to find all +directories while avoiding to stat() each entry. + +If the system returns type information in readdir, then this is used +to find directories directly. Otherwise, likely directories are names +beginning with ".", or otherwise names with no dots, of which names with +short names are tried first. + +=item EIO_READDIR_STAT_ORDER + +When this flag is specified, then the names will be returned in an order +suitable for stat()'ing each one. That is, when you plan to stat() +all files in the given directory, then the returned order will likely +be fastest. + +If both this flag and C are specified, then the +likely directories come first, resulting in a less optimal stat order. + +=item EIO_READDIR_FOUND_UNKNOWN + +This flag should not be specified when calling C. Instead, +it is being set by C (you can access the C via C<< +req->int1 >>, when any of the C's found were C. The +absence of this flag therefore indicates that all C's are known, +which can be used to speed up some algorithms. + +A typical use case would be to identify all subdirectories within a +directory - you would ask C for C. If +then this flag is I set, then all the entries at the beginning of the +returned array of type C are the directories. Otherwise, you +should start C'ing the entries starting at the beginning of the +array, stopping as soon as you found all directories (the count can be +deduced by the link count of the directory). + +=back + +=back + +=head3 OS-SPECIFIC CALL WRAPPERS + +These wrap OS-specific calls (usually Linux ones), and might or might not +be emulated on other operating systems. Calls that are not emulated will +return C<-1> and set C to C. + +=over 4 + +=item eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) + +Wraps the C syscall. The arguments follow the Linux version, but +libeio supports and will use similar calls on FreeBSD, HP/UX, Solaris and +Darwin. + +If the OS doesn't support some sendfile-like call, or the call fails, +indicating support for the given file descriptor type (for example, +Linux's sendfile might not support file to file copies), then libeio will +emulate the call in userspace, so there are almost no limitations on its +use. + +=item eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) + +Calls C. If the syscall is missing, then the call is +emulated by simply reading the data (currently in 64kiB chunks). + +=item eio_syncfs (int fd, int pri, eio_cb cb, void *data) + +Calls Linux' C syscall, if available. Returns C<-1> and sets +C to C if the call is missing I, +if the C is C<< >= 0 >>, so you can probe for the availability of the +syscall with a negative C argument and checking for C<-1/ENOSYS>. + +=item eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) + +Calls C. If the syscall is missing, then this is the same +as calling C. + +Flags can be any combination of C, +C and C. + +=item eio_fallocate (int fd, int mode, off_t offset, off_t len, int pri, eio_cb cb, void *data) + +Calls C (note: I C!). If the syscall is +missing, then it returns failure and sets C to C. + +The C argument can be C<0> (for behaviour similar to +C), or C, which keeps the size +of the file unchanged (but still preallocates space beyond end of file). + +=back + +=head3 LIBEIO-SPECIFIC REQUESTS + +These requests are specific to libeio and do not correspond to any OS call. + +=over 4 + +=item eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) + +Reads (C) or modifies (C as parameter and is expected to read +and modify any request-specific members. Specifically, it should set C<< +req->result >> to the result value, just like other requests. + +Here is an example that simply calls C, like C, but it +uses the C member as filename and uses a hardcoded C. If +you want to pass more/other parameters, you either need to pass some +struct or so via C or provide your own wrapper using the low-level +API. + + static int + my_open_done (eio_req *req) + { + int fd = req->result; + + return 0; + } + + static void + my_open (eio_req *req) + { + req->result = open (req->data, O_RDONLY); + } + + eio_custom (my_open, 0, my_open_done, "/etc/passwd"); + +=item eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data) + +This is a request that takes C seconds to execute, but otherwise +does nothing - it simply puts one of the worker threads to sleep for this +long. + +This request can be used to artificially increase load, e.g. for debugging +or benchmarking reasons. + +=item eio_nop (int pri, eio_cb cb, void *data) + +This request does nothing, except go through the whole request cycle. This +can be used to measure latency or in some cases to simplify code, but is +not really of much use. + +=back + +=head3 GROUPING AND LIMITING REQUESTS + +There is one more rather special request, C. It is a very special +aio request: Instead of doing something, it is a container for other eio +requests. + +There are two primary use cases for this: a) bundle many requests into a +single, composite, request with a definite callback and the ability to +cancel the whole request with its subrequests and b) limiting the number +of "active" requests. + +Further below you will find more discussion of these topics - first +follows the reference section detailing the request generator and other +methods. + +=over 4 + +=item eio_req *grp = eio_grp (eio_cb cb, void *data) + +Creates, submits and returns a group request. Note that it doesn't have a +priority, unlike all other requests. + +=item eio_grp_add (eio_req *grp, eio_req *req) + +Adds a request to the request group. + +=item eio_grp_cancel (eio_req *grp) + +Cancels all requests I the group, but I the group request +itself. You can cancel the group request I all subrequests via a +normal C call. + +=back + +=head4 GROUP REQUEST LIFETIME + +Left alone, a group request will instantly move to the pending state and +will be finished at the next call of C. + +The usefulness stems from the fact that, if a subrequest is added to a +group I a call to C, via C, then the group +will not finish until all the subrequests have finished. + +So the usage cycle of a group request is like this: after it is created, +you normally instantly add a subrequest. If none is added, the group +request will finish on it's own. As long as subrequests are added before +the group request is finished it will be kept from finishing, that is the +callbacks of any subrequests can, in turn, add more requests to the group, +and as long as any requests are active, the group request itself will not +finish. + +=head4 CREATING COMPOSITE REQUESTS + +Imagine you wanted to create an C request that opens a file, +reads it and closes it. This means it has to execute at least three eio +requests, but for various reasons it might be nice if that request looked +like any other eio request. + +This can be done with groups: + +=over 4 + +=item 1) create the request object + +Create a group that contains all further requests. This is the request you +can return as "the load request". + +=item 2) open the file, maybe + +Next, open the file with C and add the request to the group +request and you are finished setting up the request. + +If, for some reason, you cannot C (path is a null ptr?) you +can set C<< grp->result >> to C<-1> to signal an error and let the group +request finish on its own. + +=item 3) open callback adds more requests + +In the open callback, if the open was not successful, copy C<< +req->errorno >> to C<< grp->errorno >> and set C<< grp->errorno >> to +C<-1> to signal an error. + +Otherwise, malloc some memory or so and issue a read request, adding the +read request to the group. + +=item 4) continue issuing requests till finished + +In the real callback, check for errors and possibly continue with +C or any other eio request in the same way. + +As soon as no new requests are added the group request will finish. Make +sure you I set C<< grp->result >> to some sensible value. + +=back + +=head4 REQUEST LIMITING + + +#TODO + +void eio_grp_limit (eio_req *grp, int limit); + + +=back + + +=head1 LOW LEVEL REQUEST API + +#TODO + + +=head1 ANATOMY AND LIFETIME OF AN EIO REQUEST + +A request is represented by a structure of type C. To initialise +it, clear it to all zero bytes: + + eio_req req; + + memset (&req, 0, sizeof (req)); + +A more common way to initialise a new C is to use C: + + eio_req *req = calloc (1, sizeof (*req)); + +In either case, libeio neither allocates, initialises or frees the +C structure for you - it merely uses it. + +zero + +#TODO + +=head2 CONFIGURATION + +The functions in this section can sometimes be useful, but the default +configuration will do in most case, so you should skip this section on +first reading. + +=over 4 + +=item eio_set_max_poll_time (eio_tstamp nseconds) + +This causes C to return after it has detected that it was +running for C seconds or longer (this number can be fractional). + +This can be used to limit the amount of time spent handling eio requests, +for example, in interactive programs, you might want to limit this time to +C<0.01> seconds or so. + +Note that: + +=over 4 + +=item a) libeio doesn't know how long your request callbacks take, so the +time spent in C is up to one callback invocation longer then +this interval. + +=item b) this is implemented by calling C after each +request, which can be costly. + +=item c) at least one request will be handled. + +=back + +=item eio_set_max_poll_reqs (unsigned int nreqs) + +When C is non-zero, then C will not handle more than +C requests per invocation. This is a less costly way to limit the +amount of work done by C then setting a time limit. + +If you know your callbacks are generally fast, you could use this to +encourage interactiveness in your programs by setting it to C<10>, C<100> +or even C<1000>. + +=item eio_set_min_parallel (unsigned int nthreads) + +Make sure libeio can handle at least this many requests in parallel. It +might be able handle more. + +=item eio_set_max_parallel (unsigned int nthreads) + +Set the maximum number of threads that libeio will spawn. + +=item eio_set_max_idle (unsigned int nthreads) + +Libeio uses threads internally to handle most requests, and will start and stop threads on demand. + +This call can be used to limit the number of idle threads (threads without +work to do): libeio will keep some threads idle in preparation for more +requests, but never longer than C threads. + +In addition to this, libeio will also stop threads when they are idle for +a few seconds, regardless of this setting. + +=item unsigned int eio_nthreads () + +Return the number of worker threads currently running. + +=item unsigned int eio_nreqs () + +Return the number of requests currently handled by libeio. This is the +total number of requests that have been submitted to libeio, but not yet +destroyed. + +=item unsigned int eio_nready () + +Returns the number of ready requests, i.e. requests that have been +submitted but have not yet entered the execution phase. + +=item unsigned int eio_npending () + +Returns the number of pending requests, i.e. requests that have been +executed and have results, but have not been finished yet by a call to +C). + +=back + +=head1 EMBEDDING + +Libeio can be embedded directly into programs. This functionality is not +documented and not (yet) officially supported. + +Note that, when including C, you are responsible for defining +the compilation environment (C<_LARGEFILE_SOURCE>, C<_GNU_SOURCE> etc.). + +If you need to know how, check the C perl module, which does +exactly that. + + +=head1 COMPILETIME CONFIGURATION + +These symbols, if used, must be defined when compiling F. + +=over 4 + +=item EIO_STACKSIZE + +This symbol governs the stack size for each eio thread. Libeio itself +was written to use very little stackspace, but when using C +requests, you might want to increase this. + +If this symbol is undefined (the default) then libeio will use its default +stack size (C currently). If it is defined, but +C<0>, then the default operating system stack size will be used. In all +other cases, the value must be an expression that evaluates to the desired +stack size. + +=back + + +=head1 PORTABILITY REQUIREMENTS + +In addition to a working ISO-C implementation, libeio relies on a few +additional extensions: + +=over 4 + +=item POSIX threads + +To be portable, this module uses threads, specifically, the POSIX threads +library must be available (and working, which partially excludes many xBSD +systems, where C is buggy). + +=item POSIX-compatible filesystem API + +This is actually a harder portability requirement: The libeio API is quite +demanding regarding POSIX API calls (symlinks, user/group management +etc.). + +=item C must hold a time value in seconds with enough accuracy + +The type C is used to represent timestamps. It is required to +have at least 51 bits of mantissa (and 9 bits of exponent), which is good +enough for at least into the year 4000. This requirement is fulfilled by +implementations implementing IEEE 754 (basically all existing ones). + +=back + +If you know of other additional requirements drop me a note. + + +=head1 AUTHOR + +Marc Lehmann . + diff --git a/src/rt/libuv/src/eio/libeio.m4 b/src/rt/libuv/src/unix/eio/libeio.m4 similarity index 78% rename from src/rt/libuv/src/eio/libeio.m4 rename to src/rt/libuv/src/unix/eio/libeio.m4 index 5302cfcc91a..59151f5348a 100644 --- a/src/rt/libuv/src/eio/libeio.m4 +++ b/src/rt/libuv/src/unix/eio/libeio.m4 @@ -1,3 +1,7 @@ +dnl openbsd in it's neverending brokenness requires stdint.h for intptr_t, +dnl but that header isn't very portable... +AC_CHECK_HEADERS([stdint.h sys/syscall.h sys/prctl.h]) + AC_SEARCH_LIBS( pthread_create, [pthread pthreads pthreadVC2], @@ -119,6 +123,41 @@ int main (void) ],ac_cv_sync_file_range=yes,ac_cv_sync_file_range=no)]) test $ac_cv_sync_file_range = yes && AC_DEFINE(HAVE_SYNC_FILE_RANGE, 1, sync_file_range(2) is available) +AC_CACHE_CHECK(for fallocate, ac_cv_fallocate, [AC_LINK_IFELSE([ +#include +int main (void) +{ + int fd = 0; + int mode = 0; + off_t offset = 1; + off_t len = 1; + int res; + res = fallocate (fd, mode, offset, len); + return 0; +} +],ac_cv_fallocate=yes,ac_cv_fallocate=no)]) +test $ac_cv_fallocate = yes && AC_DEFINE(HAVE_FALLOCATE, 1, fallocate(2) is available) + +AC_CACHE_CHECK(for sys_syncfs, ac_cv_sys_syncfs, [AC_LINK_IFELSE([ +#include +#include +int main (void) +{ + int res = syscall (__NR_syncfs, (int)0); +} +],ac_cv_sys_syncfs=yes,ac_cv_sys_syncfs=no)]) +test $ac_cv_sys_syncfs = yes && AC_DEFINE(HAVE_SYS_SYNCFS, 1, syscall(__NR_syncfs) is available) + +AC_CACHE_CHECK(for prctl_set_name, ac_cv_prctl_set_name, [AC_LINK_IFELSE([ +#include +int main (void) +{ + char name[] = "test123"; + int res = prctl (PR_SET_NAME, (unsigned long)name, 0, 0, 0); +} +],ac_cv_prctl_set_name=yes,ac_cv_prctl_set_name=no)]) +test $ac_cv_prctl_set_name = yes && AC_DEFINE(HAVE_PRCTL_SET_NAME, 1, prctl(PR_SET_NAME) is available) + dnl ############################################################################# dnl # these checks exist for the benefit of IO::AIO diff --git a/src/rt/libuv/src/eio/xthread.h b/src/rt/libuv/src/unix/eio/xthread.h similarity index 76% rename from src/rt/libuv/src/eio/xthread.h rename to src/rt/libuv/src/unix/eio/xthread.h index 01e2933507d..7184c7bb73f 100644 --- a/src/rt/libuv/src/eio/xthread.h +++ b/src/rt/libuv/src/unix/eio/xthread.h @@ -2,7 +2,7 @@ #define XTHREAD_H_ /* whether word reads are potentially non-atomic. - * this is conservatice, likely most arches this runs + * this is conservative, likely most arches this runs * on have atomic word read/writes. */ #ifndef WORDACCESS_UNSAFE @@ -17,14 +17,6 @@ #ifdef _WIN32 -#ifndef __MINGW32__ -typedef int ssize_t -#endif - -#define NTDDI_VERSION NTDDI_WIN2K // needed to get win2000 api calls -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x400 -#endif #include //D #include #include @@ -34,18 +26,20 @@ typedef int ssize_t #include #include #define sigset_t int +#define sigfillset(a) #define pthread_sigmask(a,b,c) #define sigaddset(a,b) #define sigemptyset(s) -#define sigfillset(s) typedef pthread_mutex_t xmutex_t; #define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +#define X_MUTEX_CREATE(mutex) pthread_mutex_init (&(mutex), 0) #define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) #define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) typedef pthread_cond_t xcond_t; #define X_COND_INIT PTHREAD_COND_INITIALIZER +#define X_COND_CREATE(cond) pthread_cond_init (&(cond), 0) #define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) #define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) #define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) @@ -100,18 +94,27 @@ thread_create (xthread_t *tid, void *(*proc)(void *), void *arg) typedef pthread_mutex_t xmutex_t; #if __linux && defined (PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) -# define X_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +# define X_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +# define X_MUTEX_CREATE(mutex) \ + do { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init (&attr); \ + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ADAPTIVE_NP); \ + pthread_mutex_init (&(mutex), &attr); \ + } while (0) #else -# define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +# define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +# define X_MUTEX_CREATE(mutex) pthread_mutex_init (&(mutex), 0) #endif -#define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) -#define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) +#define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) +#define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) typedef pthread_cond_t xcond_t; -#define X_COND_INIT PTHREAD_COND_INITIALIZER -#define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) -#define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) -#define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) +#define X_COND_INIT PTHREAD_COND_INITIALIZER +#define X_COND_CREATE(cond) pthread_cond_init (&(cond), 0) +#define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) +#define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) +#define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) typedef pthread_t xthread_t; #define X_THREAD_PROC(name) static void *name (void *thr_arg) @@ -122,8 +125,8 @@ typedef pthread_t xthread_t; # define PTHREAD_STACK_MIN 0 #endif -#ifndef X_STACKSIZE -# define X_STACKSIZE sizeof (long) * 4096 +#ifndef XTHREAD_STACKSIZE +# define XTHREAD_STACKSIZE sizeof (void *) * 4096 #endif static int diff --git a/src/rt/libuv/src/unix/error.c b/src/rt/libuv/src/unix/error.c new file mode 100644 index 00000000000..3520eec719b --- /dev/null +++ b/src/rt/libuv/src/unix/error.c @@ -0,0 +1,110 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * TODO Share this code with Windows. + * See https://github.com/joyent/libuv/issues/76 + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include + + +/* TODO Expose callback to user to handle fatal error like V8 does. */ +void uv_fatal_error(const int errorno, const char* syscall) { + char* buf = NULL; + const char* errmsg; + + if (buf) { + errmsg = buf; + } else { + errmsg = "Unknown error"; + } + + if (syscall) { + fprintf(stderr, "\nlibuv fatal error. %s: (%d) %s\n", syscall, errorno, + errmsg); + } else { + fprintf(stderr, "\nlibuv fatal error. (%d) %s\n", errorno, errmsg); + } + + abort(); +} + + +uv_err_t uv_last_error(uv_loop_t* loop) { + return loop->last_err; +} + + +char* uv_strerror(uv_err_t err) { + return strerror(err.sys_errno_); +} + + +uv_err_code uv_translate_sys_error(int sys_errno) { + switch (sys_errno) { + case 0: return UV_OK; + case ENOENT: return UV_ENOENT; + case EACCES: return UV_EACCESS; + case EBADF: return UV_EBADF; + case EPIPE: return UV_EPIPE; + case EAGAIN: return UV_EAGAIN; + case ECONNRESET: return UV_ECONNRESET; + case EFAULT: return UV_EFAULT; + case EMFILE: return UV_EMFILE; + case EMSGSIZE: return UV_EMSGSIZE; + case EINVAL: return UV_EINVAL; + case ECONNREFUSED: return UV_ECONNREFUSED; + case EADDRINUSE: return UV_EADDRINUSE; + case EADDRNOTAVAIL: return UV_EADDRNOTAVAIL; + case ENOTCONN: return UV_ENOTCONN; + case EEXIST: return UV_EEXIST; + default: return UV_UNKNOWN; + } + + assert(0 && "unreachable"); + return -1; +} + + +uv_err_t uv_err_new_artificial(uv_loop_t* loop, int code) { + uv_err_t err; + err.sys_errno_ = 0; + err.code = code; + loop->last_err = err; + return err; +} + + +uv_err_t uv_err_new(uv_loop_t* loop, int sys_error) { + uv_err_t err; + err.sys_errno_ = sys_error; + err.code = uv_translate_sys_error(sys_error); + loop->last_err = err; + return err; +} diff --git a/src/rt/libuv/src/ev/Changes b/src/rt/libuv/src/unix/ev/Changes similarity index 100% rename from src/rt/libuv/src/ev/Changes rename to src/rt/libuv/src/unix/ev/Changes diff --git a/src/rt/libuv/src/ev/LICENSE b/src/rt/libuv/src/unix/ev/LICENSE similarity index 100% rename from src/rt/libuv/src/ev/LICENSE rename to src/rt/libuv/src/unix/ev/LICENSE diff --git a/src/rt/libuv/src/ev/Makefile.am b/src/rt/libuv/src/unix/ev/Makefile.am similarity index 100% rename from src/rt/libuv/src/ev/Makefile.am rename to src/rt/libuv/src/unix/ev/Makefile.am diff --git a/src/rt/libuv/src/ev/Makefile.in b/src/rt/libuv/src/unix/ev/Makefile.in similarity index 100% rename from src/rt/libuv/src/ev/Makefile.in rename to src/rt/libuv/src/unix/ev/Makefile.in diff --git a/src/rt/libuv/src/ev/README b/src/rt/libuv/src/unix/ev/README similarity index 100% rename from src/rt/libuv/src/ev/README rename to src/rt/libuv/src/unix/ev/README diff --git a/src/rt/libuv/src/ev/aclocal.m4 b/src/rt/libuv/src/unix/ev/aclocal.m4 similarity index 100% rename from src/rt/libuv/src/ev/aclocal.m4 rename to src/rt/libuv/src/unix/ev/aclocal.m4 diff --git a/src/rt/libuv/src/ev/autogen.sh b/src/rt/libuv/src/unix/ev/autogen.sh similarity index 100% rename from src/rt/libuv/src/ev/autogen.sh rename to src/rt/libuv/src/unix/ev/autogen.sh diff --git a/src/rt/libuv/src/ev/config.guess b/src/rt/libuv/src/unix/ev/config.guess similarity index 100% rename from src/rt/libuv/src/ev/config.guess rename to src/rt/libuv/src/unix/ev/config.guess diff --git a/src/rt/libuv/src/ev/config.h.in b/src/rt/libuv/src/unix/ev/config.h.in similarity index 100% rename from src/rt/libuv/src/ev/config.h.in rename to src/rt/libuv/src/unix/ev/config.h.in diff --git a/src/rt/libuv/src/ev/config.sub b/src/rt/libuv/src/unix/ev/config.sub similarity index 100% rename from src/rt/libuv/src/ev/config.sub rename to src/rt/libuv/src/unix/ev/config.sub diff --git a/src/rt/libuv/src/ev/config_cygwin.h b/src/rt/libuv/src/unix/ev/config_cygwin.h similarity index 100% rename from src/rt/libuv/src/ev/config_cygwin.h rename to src/rt/libuv/src/unix/ev/config_cygwin.h diff --git a/src/rt/libuv/src/ev/config_darwin.h b/src/rt/libuv/src/unix/ev/config_darwin.h similarity index 100% rename from src/rt/libuv/src/ev/config_darwin.h rename to src/rt/libuv/src/unix/ev/config_darwin.h diff --git a/src/rt/libuv/src/ev/config_freebsd.h b/src/rt/libuv/src/unix/ev/config_freebsd.h similarity index 100% rename from src/rt/libuv/src/ev/config_freebsd.h rename to src/rt/libuv/src/unix/ev/config_freebsd.h diff --git a/src/rt/libuv/src/ev/config_linux.h b/src/rt/libuv/src/unix/ev/config_linux.h similarity index 80% rename from src/rt/libuv/src/ev/config_linux.h rename to src/rt/libuv/src/unix/ev/config_linux.h index b147b591c43..a13b179f280 100644 --- a/src/rt/libuv/src/ev/config_linux.h +++ b/src/rt/libuv/src/unix/ev/config_linux.h @@ -2,12 +2,7 @@ /* config.h.in. Generated from configure.ac by autoheader. */ #include - -#define LINUX_VERSION_CODE_FOR(major, minor, patch) \ - (((major & 255) << 16) | ((minor & 255) << 8) | (patch & 255)) - -#define LINUX_VERSION_AT_LEAST(major, minor, patch) \ - (LINUX_VERSION_CODE >= LINUX_VERSION_CODE_FOR(major, minor, patch)) +#include /* Define to 1 if you have the `clock_gettime' function. */ /* #undef HAVE_CLOCK_GETTIME */ @@ -18,14 +13,26 @@ /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 -/* Define to 1 if you have the `epoll_ctl' function. */ +/* epoll_ctl(2) is available if kernel >= 2.6.9 and glibc >= 2.4 */ +#if LINUX_VERSION_CODE >= 0x020609 && __GLIBC_PREREQ(2, 4) #define HAVE_EPOLL_CTL 1 +#else +#define HAVE_EPOLL_CTL 0 +#endif -/* Define to 1 if you have the `eventfd' function. */ -#define HAVE_EVENTFD LINUX_VERSION_AT_LEAST(2, 6, 22) +/* eventfd(2) is available if kernel >= 2.6.22 and glibc >= 2.8 */ +#if LINUX_VERSION_CODE >= 0x020616 && __GLIBC_PREREQ(2, 8) +#define HAVE_EVENTFD 1 +#else +#define HAVE_EVENTFD 0 +#endif -/* Define to 1 if you have the `inotify_init' function. */ -#define HAVE_INOTIFY_INIT LINUX_VERSION_AT_LEAST(2, 6, 13) +/* inotify_init(2) is available if kernel >= 2.6.13 and glibc >= 2.4 */ +#if LINUX_VERSION_CODE >= 0x02060d && __GLIBC_PREREQ(2, 4) +#define HAVE_INOTIFY_INIT 1 +#else +#define HAVE_INOTIFY_INIT 0 +#endif /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 @@ -60,8 +67,12 @@ /* Define to 1 if you have the `select' function. */ #define HAVE_SELECT 1 -/* Define to 1 if you have the `signalfd' function. */ -#define HAVE_SIGNALFD LINUX_VERSION_AT_LEAST(2, 6, 22) +/* signalfd(2) is available if kernel >= 2.6.22 and glibc >= 2.8 */ +#if LINUX_VERSION_CODE >= 0x020616 && __GLIBC_PREREQ(2, 8) +#define HAVE_SIGNALFD 1 +#else +#define HAVE_SIGNALFD 0 +#endif /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 diff --git a/src/rt/libuv/src/unix/ev/config_netbsd.h b/src/rt/libuv/src/unix/ev/config_netbsd.h new file mode 100644 index 00000000000..ebebd4156d2 --- /dev/null +++ b/src/rt/libuv/src/unix/ev/config_netbsd.h @@ -0,0 +1,120 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* "use syscall interface for clock_gettime" */ +/* #undef HAVE_CLOCK_SYSCALL */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef HAVE_EPOLL_CTL */ + +/* Define to 1 if you have the `eventfd' function. */ +/* #undef HAVE_EVENTFD */ + +/* Define to 1 if you have the `inotify_init' function. */ +/* #undef HAVE_INOTIFY_INIT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `kqueue' function. */ +#define HAVE_KQUEUE 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef HAVE_LIBRT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `nanosleep' function. */ +/* #undef HAVE_NANOSLEEP */ + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PORT_H */ + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EVENTFD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EVENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_INOTIFY_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_QUEUE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libev" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "3.9" diff --git a/src/rt/libuv/src/ev/config_sunos.h b/src/rt/libuv/src/unix/ev/config_sunos.h similarity index 100% rename from src/rt/libuv/src/ev/config_sunos.h rename to src/rt/libuv/src/unix/ev/config_sunos.h diff --git a/src/rt/libuv/src/ev/configure b/src/rt/libuv/src/unix/ev/configure similarity index 100% rename from src/rt/libuv/src/ev/configure rename to src/rt/libuv/src/unix/ev/configure diff --git a/src/rt/libuv/src/ev/configure.ac b/src/rt/libuv/src/unix/ev/configure.ac similarity index 100% rename from src/rt/libuv/src/ev/configure.ac rename to src/rt/libuv/src/unix/ev/configure.ac diff --git a/src/rt/libuv/src/ev/depcomp b/src/rt/libuv/src/unix/ev/depcomp similarity index 100% rename from src/rt/libuv/src/ev/depcomp rename to src/rt/libuv/src/unix/ev/depcomp diff --git a/src/rt/libuv/src/ev/ev++.h b/src/rt/libuv/src/unix/ev/ev++.h similarity index 100% rename from src/rt/libuv/src/ev/ev++.h rename to src/rt/libuv/src/unix/ev/ev++.h diff --git a/src/rt/libuv/src/ev/ev.3 b/src/rt/libuv/src/unix/ev/ev.3 similarity index 100% rename from src/rt/libuv/src/ev/ev.3 rename to src/rt/libuv/src/unix/ev/ev.3 diff --git a/src/rt/libuv/src/ev/ev.c b/src/rt/libuv/src/unix/ev/ev.c similarity index 99% rename from src/rt/libuv/src/ev/ev.c rename to src/rt/libuv/src/unix/ev/ev.c index 4187b18af73..5e616c254fc 100644 --- a/src/rt/libuv/src/ev/ev.c +++ b/src/rt/libuv/src/unix/ev/ev.c @@ -3846,7 +3846,7 @@ ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) #if EV_IDLE_ENABLE if (types & EV_IDLE) - for (j = NUMPRI; i--; ) + for (j = NUMPRI; j--; ) for (i = idlecnt [j]; i--; ) cb (EV_A_ EV_IDLE, idles [j][i]); #endif diff --git a/src/rt/libuv/src/ev/ev.pod b/src/rt/libuv/src/unix/ev/ev.pod similarity index 100% rename from src/rt/libuv/src/ev/ev.pod rename to src/rt/libuv/src/unix/ev/ev.pod diff --git a/src/rt/libuv/src/ev/ev_epoll.c b/src/rt/libuv/src/unix/ev/ev_epoll.c similarity index 100% rename from src/rt/libuv/src/ev/ev_epoll.c rename to src/rt/libuv/src/unix/ev/ev_epoll.c diff --git a/src/rt/libuv/src/ev/ev_kqueue.c b/src/rt/libuv/src/unix/ev/ev_kqueue.c similarity index 100% rename from src/rt/libuv/src/ev/ev_kqueue.c rename to src/rt/libuv/src/unix/ev/ev_kqueue.c diff --git a/src/rt/libuv/src/ev/ev_poll.c b/src/rt/libuv/src/unix/ev/ev_poll.c similarity index 100% rename from src/rt/libuv/src/ev/ev_poll.c rename to src/rt/libuv/src/unix/ev/ev_poll.c diff --git a/src/rt/libuv/src/ev/ev_port.c b/src/rt/libuv/src/unix/ev/ev_port.c similarity index 100% rename from src/rt/libuv/src/ev/ev_port.c rename to src/rt/libuv/src/unix/ev/ev_port.c diff --git a/src/rt/libuv/src/ev/ev_select.c b/src/rt/libuv/src/unix/ev/ev_select.c similarity index 100% rename from src/rt/libuv/src/ev/ev_select.c rename to src/rt/libuv/src/unix/ev/ev_select.c diff --git a/src/rt/libuv/src/ev/ev_vars.h b/src/rt/libuv/src/unix/ev/ev_vars.h similarity index 100% rename from src/rt/libuv/src/ev/ev_vars.h rename to src/rt/libuv/src/unix/ev/ev_vars.h diff --git a/src/rt/libuv/src/ev/ev_win32.c b/src/rt/libuv/src/unix/ev/ev_win32.c similarity index 100% rename from src/rt/libuv/src/ev/ev_win32.c rename to src/rt/libuv/src/unix/ev/ev_win32.c diff --git a/src/rt/libuv/src/ev/ev_wrap.h b/src/rt/libuv/src/unix/ev/ev_wrap.h similarity index 100% rename from src/rt/libuv/src/ev/ev_wrap.h rename to src/rt/libuv/src/unix/ev/ev_wrap.h diff --git a/src/rt/libuv/src/ev/event.c b/src/rt/libuv/src/unix/ev/event.c similarity index 100% rename from src/rt/libuv/src/ev/event.c rename to src/rt/libuv/src/unix/ev/event.c diff --git a/src/rt/libuv/src/ev/event.h b/src/rt/libuv/src/unix/ev/event.h similarity index 100% rename from src/rt/libuv/src/ev/event.h rename to src/rt/libuv/src/unix/ev/event.h diff --git a/src/rt/libuv/src/ev/install-sh b/src/rt/libuv/src/unix/ev/install-sh similarity index 100% rename from src/rt/libuv/src/ev/install-sh rename to src/rt/libuv/src/unix/ev/install-sh diff --git a/src/rt/libuv/src/ev/libev.m4 b/src/rt/libuv/src/unix/ev/libev.m4 similarity index 100% rename from src/rt/libuv/src/ev/libev.m4 rename to src/rt/libuv/src/unix/ev/libev.m4 diff --git a/src/rt/libuv/src/ev/ltmain.sh b/src/rt/libuv/src/unix/ev/ltmain.sh similarity index 100% rename from src/rt/libuv/src/ev/ltmain.sh rename to src/rt/libuv/src/unix/ev/ltmain.sh diff --git a/src/rt/libuv/src/ev/missing b/src/rt/libuv/src/unix/ev/missing similarity index 100% rename from src/rt/libuv/src/ev/missing rename to src/rt/libuv/src/unix/ev/missing diff --git a/src/rt/libuv/src/ev/mkinstalldirs b/src/rt/libuv/src/unix/ev/mkinstalldirs similarity index 100% rename from src/rt/libuv/src/ev/mkinstalldirs rename to src/rt/libuv/src/unix/ev/mkinstalldirs diff --git a/src/rt/libuv/src/uv-linux.c b/src/rt/libuv/src/unix/freebsd.c similarity index 69% rename from src/rt/libuv/src/uv-linux.c rename to src/rt/libuv/src/unix/freebsd.c index dc718c6decd..449aad4c211 100644 --- a/src/rt/libuv/src/uv-linux.c +++ b/src/rt/libuv/src/unix/freebsd.c @@ -20,18 +20,19 @@ #include "uv.h" -#include -#include +#include +#include +#include + +#include +#include #include #undef NANOSEC #define NANOSEC 1000000000 -/* - * There's probably some way to get time from Linux than gettimeofday(). What - * it is, I don't know. - */ -uint64_t uv_hrtime() { + +uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * NANOSEC + ts.tv_nsec); @@ -43,13 +44,39 @@ int uv_exepath(char* buffer, size_t* size) { int result; char* path; char* fullpath; + int mib[4]; + size_t cb; if (!buffer || !size) { return -1; } - *size = readlink("/proc/self/exe", buffer, *size - 1); - if (*size <= 0) return -1; - buffer[*size] = '\0'; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + + cb = *size; + if (sysctl(mib, 4, buffer, &cb, NULL, 0) < 0) { + *size = 0; + return -1; + } + *size = strlen(buffer); + return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/fs.c b/src/rt/libuv/src/unix/fs.c new file mode 100644 index 00000000000..d745622835b --- /dev/null +++ b/src/rt/libuv/src/unix/fs.c @@ -0,0 +1,697 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" +#include "eio.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define ARGS1(a) (a) +#define ARGS2(a,b) (a), (b) +#define ARGS3(a,b,c) (a), (b), (c) +#define ARGS4(a,b,c,d) (a), (b), (c), (d) + +#define WRAP_EIO(type, eiofunc, func, args) \ + uv_fs_req_init(loop, req, type, path, cb); \ + if (cb) { \ + /* async */ \ + req->eio = eiofunc(args, EIO_PRI_DEFAULT, uv__fs_after, req); \ + if (!req->eio) { \ + uv_err_new(loop, ENOMEM); \ + return -1; \ + } \ + uv_ref(loop); \ + } else { \ + /* sync */ \ + req->result = func(args); \ + if (req->result) { \ + uv_err_new(loop, errno); \ + } \ + return req->result; \ + } \ + return 0; + + +static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, uv_fs_type fs_type, + const char* path, uv_fs_cb cb) { + /* Make sure the thread pool is initialized. */ + uv_eio_init(loop); + + uv__req_init((uv_req_t*) req); + req->type = UV_FS; + req->loop = loop; + req->fs_type = fs_type; + req->cb = cb; + req->result = 0; + req->ptr = NULL; + req->path = path ? strdup(path) : NULL; + req->errorno = 0; + req->eio = NULL; +} + + +void uv_fs_req_cleanup(uv_fs_t* req) { + free(req->path); + req->path = NULL; + + switch (req->fs_type) { + case UV_FS_READDIR: + assert((req->result == -1 && req->ptr == NULL) + || (req->result >= 0 && req->ptr != NULL)); + free(req->ptr); + req->ptr = NULL; + break; + + case UV_FS_STAT: + case UV_FS_LSTAT: + req->ptr = NULL; + break; + + default: + break; + } +} + + +static int uv__fs_after(eio_req* eio) { + char* name; + int namelen; + int buflen = 0; + uv_fs_t* req = eio->data; + int i; + + assert(req->cb); + + req->result = req->eio->result; + req->errorno = uv_translate_sys_error(req->eio->errorno); + + switch (req->fs_type) { + case UV_FS_READDIR: + if (req->eio->result == -1) + break; /* opendir() or readdir() operation failed. */ + + /* + * XXX This is pretty bad. + * We alloc and copy the large null terminated string list from libeio. + * This is done because libeio is going to free eio->ptr2 after this + * callback. We must keep it until uv_fs_req_cleanup. If we get rid of + * libeio this can be avoided. + */ + buflen = 0; + name = req->eio->ptr2; + for (i = 0; i < req->result; i++) { + namelen = strlen(name); + buflen += namelen + 1; + /* TODO check ENOMEM */ + name += namelen; + assert(*name == '\0'); + name++; + } + req->ptr = malloc(buflen); + memcpy(req->ptr, req->eio->ptr2, buflen); + break; + + case UV_FS_STAT: + case UV_FS_LSTAT: + case UV_FS_FSTAT: + req->ptr = req->eio->ptr2; + break; + + case UV_FS_READLINK: + if (req->result == -1) { + req->ptr = NULL; + } else { + assert(req->result > 0); + + if ((name = realloc(req->eio->ptr2, req->result + 1)) == NULL) { + /* Not enough memory. Reuse buffer, chop off last byte. */ + name = req->eio->ptr2; + req->result--; + } + + name[req->result] = '\0'; + req->ptr = name; + req->result = 0; + } + break; + + default: + break; + } + + uv_unref(req->loop); + req->eio = NULL; /* Freed by libeio */ + + req->cb(req); + return 0; +} + + +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_CLOSE, eio_close, close, ARGS1(file)); +} + + +int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + int mode, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_OPEN, path, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_open(path, flags, mode, EIO_PRI_DEFAULT, uv__fs_after, req); + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = open(path, flags, mode); + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + uv__cloexec(req->result, 1); + + return req->result; + } + + return 0; +} + + +int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file fd, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_READ, NULL, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_read(fd, buf, length, offset, EIO_PRI_DEFAULT, + uv__fs_after, req); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = offset < 0 ? + read(fd, buf, length) : + pread(fd, buf, length, offset); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + return req->result; + } + + return 0; +} + + +int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + WRAP_EIO(UV_FS_UNLINK, eio_unlink, unlink, ARGS1(path)) +} + + +int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_WRITE, NULL, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_write(file, buf, length, offset, EIO_PRI_DEFAULT, + uv__fs_after, req); + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = offset < 0 ? + write(file, buf, length) : + pwrite(file, buf, length, offset); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + return req->result; + } + + return 0; +} + + +int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + WRAP_EIO(UV_FS_MKDIR, eio_mkdir, mkdir, ARGS2(path, mode)) +} + + +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + WRAP_EIO(UV_FS_RMDIR, eio_rmdir, rmdir, ARGS1(path)) +} + + +int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + uv_fs_cb cb) { + int r; + struct dirent* entry; + size_t size = 0; + size_t d_namlen = 0; + + uv_fs_req_init(loop, req, UV_FS_READDIR, path, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_readdir(path, flags, EIO_PRI_DEFAULT, uv__fs_after, req); + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + DIR* dir = opendir(path); + if (!dir) { + uv_err_new(loop, errno); + req->result = -1; + return -1; + } + + /* req->result stores number of entries */ + req->result = 0; + + while ((entry = readdir(dir))) { + d_namlen = strlen(entry->d_name); + + /* Skip . and .. */ + if ((d_namlen == 1 && entry->d_name[0] == '.') || + (d_namlen == 2 && entry->d_name[0] == '.' && + entry->d_name[1] == '.')) { + continue; + } + + req->ptr = realloc(req->ptr, size + d_namlen + 1); + /* TODO check ENOMEM */ + memcpy((char*)req->ptr + size, entry->d_name, d_namlen); + size += d_namlen; + ((char*)req->ptr)[size] = '\0'; + size++; + req->result++; + } + + r = closedir(dir); + if (r) { + uv_err_new(loop, errno); + req->result = -1; + return -1; + } + + return req->result; + } + + return 0; +} + + +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + char* pathdup; + int pathlen; + + uv_fs_req_init(loop, req, UV_FS_STAT, path, cb); + + /* TODO do this without duplicating the string. */ + /* TODO security */ + pathdup = strdup(path); + pathlen = strlen(path); + + if (pathlen > 0 && path[pathlen - 1] == '\\') { + /* TODO do not modify input string */ + pathdup[pathlen - 1] = '\0'; + } + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_stat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + + free(pathdup); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = stat(pathdup, &req->statbuf); + + free(pathdup); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + req->ptr = &req->statbuf; + return req->result; + } + + return 0; +} + + +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_FSTAT, NULL, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_fstat(file, EIO_PRI_DEFAULT, uv__fs_after, req); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = fstat(file, &req->statbuf); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + req->ptr = &req->statbuf; + return req->result; + } + + return 0; +} + + +int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, + uv_fs_cb cb) { + WRAP_EIO(UV_FS_RENAME, eio_rename, rename, ARGS2(path, new_path)) +} + + +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FSYNC, eio_fsync, fsync, ARGS1(file)) +} + + +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + char* path = NULL; +#ifdef __FreeBSD__ + /* freebsd doesn't have fdatasync, do a full fsync instead. */ + WRAP_EIO(UV_FS_FDATASYNC, eio_fdatasync, fsync, ARGS1(file)) +#else + WRAP_EIO(UV_FS_FDATASYNC, eio_fdatasync, fdatasync, ARGS1(file)) +#endif +} + + +int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, off_t offset, + uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FTRUNCATE, eio_ftruncate, ftruncate, ARGS2(file, offset)) +} + + +int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, + off_t in_offset, size_t length, uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_SENDFILE, eio_sendfile, eio_sendfile_sync, + ARGS4(out_fd, in_fd, in_offset, length)) +} + + +int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + WRAP_EIO(UV_FS_CHMOD, eio_chmod, chmod, ARGS2(path, mode)) +} + + +static int _utime(const char* path, double atime, double mtime) { + struct utimbuf buf; + buf.actime = atime; + buf.modtime = mtime; + return utime(path, &buf); +} + + +int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb) { + WRAP_EIO(UV_FS_UTIME, eio_utime, _utime, ARGS3(path, atime, mtime)) +} + + +#if defined(HAVE_FUTIMES) +static int _futime(const uv_file file, double atime, double mtime) { + struct timeval tv[2]; + + /* FIXME possible loss of precision in floating-point arithmetic? */ + tv[0].tv_sec = atime; + tv[0].tv_usec = (unsigned long)(atime * 1000000) % 1000000; + + tv[1].tv_sec = mtime; + tv[1].tv_usec = (unsigned long)(mtime * 1000000) % 1000000; + + return futimes(file, tv); +} +#endif + + +int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, + double mtime, uv_fs_cb cb) { +#if defined(HAVE_FUTIMES) + const char* path = NULL; + + uv_fs_req_init(loop, req, UV_FS_FUTIME, path, cb); + + WRAP_EIO(UV_FS_FUTIME, eio_futime, _futime, ARGS3(file, atime, mtime)) +#else + uv_err_new(loop, ENOSYS); + return -1; +#endif +} + + +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + char* pathdup; + int pathlen; + + uv_fs_req_init(loop, req, UV_FS_LSTAT, path, cb); + + /* TODO do this without duplicating the string. */ + /* TODO security */ + pathdup = strdup(path); + pathlen = strlen(path); + + if (pathlen > 0 && path[pathlen - 1] == '\\') { + /* TODO do not modify input string */ + pathdup[pathlen - 1] = '\0'; + } + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_lstat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + + free(pathdup); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = lstat(pathdup, &req->statbuf); + + free(pathdup); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + req->ptr = &req->statbuf; + return req->result; + } + + return 0; +} + + +int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb) { + WRAP_EIO(UV_FS_LINK, eio_link, link, ARGS2(path, new_path)) +} + + +int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, int flags, uv_fs_cb cb) { + WRAP_EIO(UV_FS_SYMLINK, eio_symlink, symlink, ARGS2(path, new_path)) +} + + +int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb) { + ssize_t size; + int status; + char* buf; + + status = -1; + + uv_fs_req_init(loop, req, UV_FS_READLINK, path, cb); + + if (cb) { + if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req))) { + uv_ref(loop); + return 0; + } else { + uv_err_new(loop, ENOMEM); + return -1; + } + } else { + /* pathconf(_PC_PATH_MAX) may return -1 to signify that path + * lengths have no upper limit or aren't suitable for malloc'ing. + */ + if ((size = pathconf(path, _PC_PATH_MAX)) == -1) { +#if defined(PATH_MAX) + size = PATH_MAX; +#else + size = 4096; +#endif + } + + if ((buf = malloc(size + 1)) == NULL) { + uv_err_new(loop, ENOMEM); + return -1; + } + + if ((size = readlink(path, buf, size)) == -1) { + req->errorno = errno; + req->result = -1; + free(buf); + } else { + /* Cannot conceivably fail since it shrinks the buffer. */ + buf = realloc(buf, size + 1); + buf[size] = '\0'; + req->result = 0; + req->ptr = buf; + } + + return req->result; + } + + assert(0 && "unreachable"); +} + + +int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, + uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FCHMOD, eio_fchmod, fchmod, ARGS2(file, mode)) +} + + +int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid, + int gid, uv_fs_cb cb) { + WRAP_EIO(UV_FS_CHOWN, eio_chown, chown, ARGS3(path, uid, gid)) +} + + +int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, int gid, + uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FCHOWN, eio_fchown, fchown, ARGS3(file, uid, gid)) +} + + +static void uv__work(eio_req* eio) { + uv_work_t* req = eio->data; + if (req->work_cb) { + req->work_cb(req); + } +} + + +static int uv__after_work(eio_req *eio) { + uv_work_t* req = eio->data; + uv_unref(req->loop); + if (req->after_work_cb) { + req->after_work_cb(req); + } + return 0; +} + + +int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, + uv_after_work_cb after_work_cb) { + void* data = req->data; + + uv_eio_init(loop); + + uv__req_init((uv_req_t*) req); + uv_ref(loop); + req->loop = loop; + req->data = data; + req->work_cb = work_cb; + req->after_work_cb = after_work_cb; + + req->eio = eio_custom(uv__work, EIO_PRI_DEFAULT, uv__after_work, req); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + return 0; +} diff --git a/src/rt/libuv/src/unix/internal.h b/src/rt/libuv/src/unix/internal.h new file mode 100644 index 00000000000..42283ca2a51 --- /dev/null +++ b/src/rt/libuv/src/unix/internal.h @@ -0,0 +1,129 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_UNIX_INTERNAL_H_ +#define UV_UNIX_INTERNAL_H_ + +#include "uv-common.h" +#include "uv-eio.h" + +#include /* offsetof */ + +#if defined(__linux__) + +#include +#include + +#undef HAVE_FUTIMES +#undef HAVE_PIPE2 +#undef HAVE_ACCEPT4 + +/* futimes() requires linux >= 2.6.22 and glib >= 2.6 */ +#if LINUX_VERSION_CODE >= 0x20616 && __GLIBC_PREREQ(2, 6) +#define HAVE_FUTIMES +#endif + +/* pipe2() requires linux >= 2.6.27 and glibc >= 2.9 */ +#if LINUX_VERSION_CODE >= 0x2061B && __GLIBC_PREREQ(2, 9) +#define HAVE_PIPE2 +#endif + +/* accept4() requires linux >= 2.6.28 and glib >= 2.10 */ +#if LINUX_VERSION_CODE >= 0x2061C && __GLIBC_PREREQ(2, 10) +#define HAVE_ACCEPT4 +#endif + +#endif /* __linux__ */ + +#ifdef __APPLE__ +# define HAVE_FUTIMES +#endif + +#ifdef __FreeBSD__ +# define HAVE_FUTIMES +#endif + +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member))) + +#define SAVE_ERRNO(block) \ + do { \ + int _saved_errno = errno; \ + do { block; } while (0); \ + errno = _saved_errno; \ + } \ + while (0); + +/* flags */ +enum { + UV_CLOSING = 0x00000001, /* uv_close() called but not finished. */ + UV_CLOSED = 0x00000002, /* close(2) finished. */ + UV_READING = 0x00000004, /* uv_read_start() called. */ + UV_SHUTTING = 0x00000008, /* uv_shutdown() called but not complete. */ + UV_SHUT = 0x00000010, /* Write side closed. */ + UV_READABLE = 0x00000020, /* The stream is readable */ + UV_WRITABLE = 0x00000040 /* The stream is writable */ +}; + +size_t uv__strlcpy(char* dst, const char* src, size_t size); + +int uv__close(int fd); +void uv__req_init(uv_req_t*); +void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type); + + +int uv__nonblock(int fd, int set) __attribute__((unused)); +int uv__cloexec(int fd, int set) __attribute__((unused)); +int uv__socket(int domain, int type, int protocol); + +/* error */ +uv_err_code uv_translate_sys_error(int sys_errno); +uv_err_t uv_err_new(uv_loop_t* loop, int sys_error); +uv_err_t uv_err_new_artificial(uv_loop_t* loop, int code); +void uv_fatal_error(const int errorno, const char* syscall); + +/* stream */ +void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, + uv_handle_type type); +int uv__stream_open(uv_stream_t*, int fd, int flags); +void uv__stream_destroy(uv_stream_t* stream); +void uv__stream_io(EV_P_ ev_io* watcher, int revents); +void uv__server_io(EV_P_ ev_io* watcher, int revents); +int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t len); +int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, + socklen_t addrlen, uv_connect_cb cb); + +/* tcp */ +int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); + +/* pipe */ +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); +void uv__pipe_accept(EV_P_ ev_io* watcher, int revents); +int uv_pipe_cleanup(uv_pipe_t* handle); + +/* udp */ +void uv__udp_destroy(uv_udp_t* handle); +void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w); + +/* fs */ +void uv__fs_event_destroy(uv_fs_event_t* handle); + +#endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/src/rt/libuv/src/unix/linux.c b/src/rt/libuv/src/unix/linux.c new file mode 100644 index 00000000000..0b4ce64ddf3 --- /dev/null +++ b/src/rt/libuv/src/unix/linux.c @@ -0,0 +1,183 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#undef NANOSEC +#define NANOSEC 1000000000 + + +/* Don't look aghast, this is exactly how glibc's basename() works. */ +static char* basename_r(const char* path) { + char* s = strrchr(path, '/'); + return s ? (s + 1) : (char*)path; +} + + +/* + * There's probably some way to get time from Linux than gettimeofday(). What + * it is, I don't know. + */ +uint64_t uv_hrtime() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (ts.tv_sec * NANOSEC + ts.tv_nsec); +} + + +int uv_exepath(char* buffer, size_t* size) { + if (!buffer || !size) { + return -1; + } + + *size = readlink("/proc/self/exe", buffer, *size - 1); + if (*size <= 0) return -1; + buffer[*size] = '\0'; + return 0; +} + + +static int new_inotify_fd(void) { +#if defined(IN_NONBLOCK) && defined(IN_CLOEXEC) + return inotify_init1(IN_NONBLOCK | IN_CLOEXEC); +#else + int fd; + + if ((fd = inotify_init()) == -1) + return -1; + + if (uv__cloexec(fd, 1) || uv__nonblock(fd, 1)) { + SAVE_ERRNO(uv__close(fd)); + fd = -1; + } + + return fd; +#endif +} + + +static void uv__inotify_read(EV_P_ ev_io* w, int revents) { + struct inotify_event* e; + uv_fs_event_t* handle; + const char* filename; + ssize_t size; + int events; + char *p; + /* needs to be large enough for sizeof(inotify_event) + strlen(filename) */ + char buf[4096]; + + handle = container_of(w, uv_fs_event_t, read_watcher); + + do { + do { + size = read(handle->fd, buf, sizeof buf); + } + while (size == -1 && errno == EINTR); + + if (size == -1) { + assert(errno == EAGAIN || errno == EWOULDBLOCK); + break; + } + + assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ + + /* Now we have one or more inotify_event structs. */ + for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { + e = (void*)p; + + events = 0; + if (e->mask & (IN_ATTRIB|IN_MODIFY)) + events |= UV_CHANGE; + if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) + events |= UV_RENAME; + + /* inotify does not return the filename when monitoring a single file + * for modifications. Repurpose the filename for API compatibility. + * I'm not convinced this is a good thing, maybe it should go. + */ + filename = e->len ? e->name : basename_r(handle->filename); + + handle->cb(handle, filename, events, 0); + } + } + while (handle->fd != -1); /* handle might've been closed by callback */ +} + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + int flags; + int fd; + + /* + * TODO share a single inotify fd across the event loop? + * We'll run into fs.inotify.max_user_instances if we + * keep creating new inotify fds. + */ + if ((fd = new_inotify_fd()) == -1) { + uv_err_new(loop, errno); + return -1; + } + + flags = IN_ATTRIB + | IN_CREATE + | IN_MODIFY + | IN_DELETE + | IN_DELETE_SELF + | IN_MOVED_FROM + | IN_MOVED_TO; + + if (inotify_add_watch(fd, filename, flags) == -1) { + uv_err_new(loop, errno); + uv__close(fd); + return -1; + } + + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + handle->filename = strdup(filename); /* this should go! */ + handle->cb = cb; + handle->fd = fd; + + ev_io_init(&handle->read_watcher, uv__inotify_read, fd, EV_READ); + ev_io_start(loop->ev, &handle->read_watcher); + + return 0; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + ev_io_stop(handle->loop->ev, &handle->read_watcher); + uv__close(handle->fd); + handle->fd = -1; + free(handle->filename); +} diff --git a/src/rt/libuv/src/unix/netbsd.c b/src/rt/libuv/src/unix/netbsd.c new file mode 100644 index 00000000000..0ba79997545 --- /dev/null +++ b/src/rt/libuv/src/unix/netbsd.c @@ -0,0 +1,85 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#undef NANOSEC +#define NANOSEC 1000000000 + + +uint64_t uv_hrtime(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (ts.tv_sec * NANOSEC + ts.tv_nsec); +} + + +int uv_exepath(char* buffer, size_t* size) { + uint32_t usize; + int result; + char* path; + char* fullpath; + int mib[4]; + size_t cb; + pid_t mypid; + + if (!buffer || !size) { + return -1; + } + + mypid = getpid(); + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = mypid; + mib[3] = KERN_PROC_ARGV; + + cb = *size; + if (sysctl(mib, 4, buffer, &cb, NULL, 0) < 0) { + *size = 0; + return -1; + } + *size = strlen(buffer); + + return 0; +} + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/pipe.c b/src/rt/libuv/src/unix/pipe.c new file mode 100644 index 00000000000..50dc635b7e4 --- /dev/null +++ b/src/rt/libuv/src/unix/pipe.c @@ -0,0 +1,272 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle) { + uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); + loop->counters.pipe_init++; + handle->pipe_fname = NULL; + return 0; +} + + +int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + struct sockaddr_un saddr; + const char* pipe_fname; + int saved_errno; + int sockfd; + int status; + int bound; + + saved_errno = errno; + pipe_fname = NULL; + sockfd = -1; + status = -1; + bound = 0; + + /* Already bound? */ + if (handle->fd >= 0) { + uv_err_new_artificial(handle->loop, UV_EINVAL); + goto out; + } + + /* Make a copy of the file name, it outlives this function's scope. */ + if ((pipe_fname = strdup(name)) == NULL) { + uv_err_new(handle->loop, ENOMEM); + goto out; + } + + /* We've got a copy, don't touch the original any more. */ + name = NULL; + + if ((sockfd = uv__socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + memset(&saddr, 0, sizeof saddr); + uv__strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + saddr.sun_family = AF_UNIX; + + if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr) == -1) { + /* On EADDRINUSE: + * + * We hold the file lock so there is no other process listening + * on the socket. Ergo, it's stale - remove it. + * + * This assumes that the other process uses locking too + * but that's a good enough assumption for now. + */ + if (errno != EADDRINUSE + || unlink(pipe_fname) == -1 + || bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr) == -1) { + /* Convert ENOENT to EACCES for compatibility with Windows. */ + uv_err_new(handle->loop, (errno == ENOENT) ? EACCES : errno); + goto out; + } + } + bound = 1; + + /* Success. */ + handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */ + handle->fd = sockfd; + status = 0; + +out: + /* Clean up on error. */ + if (status) { + if (bound) { + /* unlink() before close() to avoid races. */ + assert(pipe_fname != NULL); + unlink(pipe_fname); + } + uv__close(sockfd); + + free((void*)pipe_fname); + } + + errno = saved_errno; + return status; +} + + +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->fd == -1) { + uv_err_new_artificial(handle->loop, UV_EINVAL); + goto out; + } + assert(handle->fd >= 0); + + if ((status = listen(handle->fd, backlog)) == -1) { + uv_err_new(handle->loop, errno); + } else { + handle->connection_cb = cb; + ev_io_init(&handle->read_watcher, uv__pipe_accept, handle->fd, EV_READ); + ev_io_start(handle->loop->ev, &handle->read_watcher); + } + +out: + errno = saved_errno; + return status; +} + + +int uv_pipe_cleanup(uv_pipe_t* handle) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->pipe_fname) { + /* + * Unlink the file system entity before closing the file descriptor. + * Doing it the other way around introduces a race where our process + * unlinks a socket with the same name that's just been created by + * another thread or process. + * + * This is less of an issue now that we attach a file lock + * to the socket but it's still a best practice. + */ + unlink(handle->pipe_fname); + free((void*)handle->pipe_fname); + } + + errno = saved_errno; + return status; +} + + +void uv_pipe_open(uv_pipe_t* handle, uv_file fd) { + uv__stream_open((uv_stream_t*)handle, fd, UV_READABLE | UV_WRITABLE); +} + + +int uv_pipe_connect(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + uv_connect_cb cb) { + struct sockaddr_un saddr; + int saved_errno; + int sockfd; + int status; + int r; + + saved_errno = errno; + sockfd = -1; + status = -1; + + if ((sockfd = uv__socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + memset(&saddr, 0, sizeof saddr); + uv__strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + saddr.sun_family = AF_UNIX; + + /* We don't check for EINPROGRESS. Think about it: the socket + * is either there or not. + */ + do { + r = connect(sockfd, (struct sockaddr*)&saddr, sizeof saddr); + } + while (r == -1 && errno == EINTR); + + if (r == -1) { + uv_err_new(handle->loop, errno); + uv__close(sockfd); + goto out; + } + + uv__stream_open((uv_stream_t*)handle, sockfd, UV_READABLE | UV_WRITABLE); + + ev_io_start(handle->loop->ev, &handle->read_watcher); + ev_io_start(handle->loop->ev, &handle->write_watcher); + + status = 0; + +out: + handle->delayed_error = status; /* Passed to callback. */ + handle->connect_req = req; + req->handle = (uv_stream_t*)handle; + req->type = UV_CONNECT; + req->cb = cb; + ngx_queue_init(&req->queue); + + /* Run callback on next tick. */ + ev_feed_event(handle->loop->ev, &handle->read_watcher, EV_CUSTOM); + assert(ev_is_pending(&handle->read_watcher)); + + /* Mimic the Windows pipe implementation, always + * return 0 and let the callback handle errors. + */ + errno = saved_errno; + return 0; +} + + +/* TODO merge with uv__server_io()? */ +void uv__pipe_accept(EV_P_ ev_io* watcher, int revents) { + struct sockaddr_un saddr; + uv_pipe_t* pipe; + int saved_errno; + int sockfd; + + saved_errno = errno; + pipe = watcher->data; + + assert(pipe->type == UV_NAMED_PIPE); + assert(pipe->pipe_fname != NULL); + + sockfd = uv__accept(pipe->fd, (struct sockaddr *)&saddr, sizeof saddr); + if (sockfd == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + assert(0 && "EAGAIN on uv__accept(pipefd)"); + } else { + uv_err_new(pipe->loop, errno); + } + } else { + pipe->accepted_fd = sockfd; + pipe->connection_cb((uv_stream_t*)pipe, 0); + if (pipe->accepted_fd == sockfd) { + /* The user hasn't yet accepted called uv_accept() */ + ev_io_stop(pipe->loop->ev, &pipe->read_watcher); + } + } + + errno = saved_errno; +} diff --git a/src/rt/libuv/src/unix/process.c b/src/rt/libuv/src/unix/process.c new file mode 100644 index 00000000000..34f12d1737f --- /dev/null +++ b/src/rt/libuv/src/unix/process.c @@ -0,0 +1,300 @@ + +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include /* O_CLOEXEC, O_NONBLOCK */ +#include +#include +#include + +#ifdef __APPLE__ +# include +# define environ (*_NSGetEnviron()) +#else +extern char **environ; +#endif + + +static void uv__chld(EV_P_ ev_child* watcher, int revents) { + int status = watcher->rstatus; + int exit_status = 0; + int term_signal = 0; + uv_process_t *process = watcher->data; + + assert(&process->child_watcher == watcher); + assert(revents & EV_CHILD); + + ev_child_stop(EV_A_ &process->child_watcher); + + if (WIFEXITED(status)) { + exit_status = WEXITSTATUS(status); + } + + if (WIFSIGNALED(status)) { + term_signal = WTERMSIG(status); + } + + if (process->exit_cb) { + process->exit_cb(process, exit_status, term_signal); + } +} + +#ifndef SPAWN_WAIT_EXEC +# define SPAWN_WAIT_EXEC 1 +#endif + +int uv_spawn(uv_loop_t* loop, uv_process_t* process, + uv_process_options_t options) { + /* + * Save environ in the case that we get it clobbered + * by the child process. + */ + char** save_our_env = environ; + int stdin_pipe[2] = { -1, -1 }; + int stdout_pipe[2] = { -1, -1 }; + int stderr_pipe[2] = { -1, -1 }; +#if SPAWN_WAIT_EXEC + int signal_pipe[2] = { -1, -1 }; + struct pollfd pfd; +#endif + int status; + pid_t pid; + + uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); + loop->counters.process_init++; + + process->exit_cb = options.exit_cb; + + if (options.stdin_stream) { + if (options.stdin_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stdin_pipe) < 0) { + goto error; + } + uv__cloexec(stdin_pipe[0], 1); + uv__cloexec(stdin_pipe[1], 1); + } + + if (options.stdout_stream) { + if (options.stdout_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stdout_pipe) < 0) { + goto error; + } + uv__cloexec(stdout_pipe[0], 1); + uv__cloexec(stdout_pipe[1], 1); + } + + if (options.stderr_stream) { + if (options.stderr_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stderr_pipe) < 0) { + goto error; + } + uv__cloexec(stderr_pipe[0], 1); + uv__cloexec(stderr_pipe[1], 1); + } + + /* This pipe is used by the parent to wait until + * the child has called `execve()`. We need this + * to avoid the following race condition: + * + * if ((pid = fork()) > 0) { + * kill(pid, SIGTERM); + * } + * else if (pid == 0) { + * execve("/bin/cat", argp, envp); + * } + * + * The parent sends a signal immediately after forking. + * Since the child may not have called `execve()` yet, + * there is no telling what process receives the signal, + * our fork or /bin/cat. + * + * To avoid ambiguity, we create a pipe with both ends + * marked close-on-exec. Then, after the call to `fork()`, + * the parent polls the read end until it sees POLLHUP. + */ +#if SPAWN_WAIT_EXEC +# ifdef HAVE_PIPE2 + if (pipe2(signal_pipe, O_CLOEXEC | O_NONBLOCK) < 0) { + goto error; + } +# else + if (pipe(signal_pipe) < 0) { + goto error; + } + uv__cloexec(signal_pipe[0], 1); + uv__cloexec(signal_pipe[1], 1); + uv__nonblock(signal_pipe[0], 1); + uv__nonblock(signal_pipe[1], 1); +# endif +#endif + + pid = fork(); + + if (pid == -1) { +#if SPAWN_WAIT_EXEC + uv__close(signal_pipe[0]); + uv__close(signal_pipe[1]); +#endif + environ = save_our_env; + goto error; + } + + if (pid == 0) { + if (stdin_pipe[0] >= 0) { + uv__close(stdin_pipe[1]); + dup2(stdin_pipe[0], STDIN_FILENO); + } else { + /* Reset flags that might be set by Node */ + uv__cloexec(STDIN_FILENO, 0); + uv__nonblock(STDIN_FILENO, 0); + } + + if (stdout_pipe[1] >= 0) { + uv__close(stdout_pipe[0]); + dup2(stdout_pipe[1], STDOUT_FILENO); + } else { + /* Reset flags that might be set by Node */ + uv__cloexec(STDOUT_FILENO, 0); + uv__nonblock(STDOUT_FILENO, 0); + } + + if (stderr_pipe[1] >= 0) { + uv__close(stderr_pipe[0]); + dup2(stderr_pipe[1], STDERR_FILENO); + } else { + /* Reset flags that might be set by Node */ + uv__cloexec(STDERR_FILENO, 0); + uv__nonblock(STDERR_FILENO, 0); + } + + if (options.cwd && chdir(options.cwd)) { + perror("chdir()"); + _exit(127); + } + + environ = options.env; + + execvp(options.file, options.args); + perror("execvp()"); + _exit(127); + /* Execution never reaches here. */ + } + + /* Parent. */ + + /* Restore environment. */ + environ = save_our_env; + +#if SPAWN_WAIT_EXEC + /* POLLHUP signals child has exited or execve()'d. */ + uv__close(signal_pipe[1]); + do { + pfd.fd = signal_pipe[0]; + pfd.events = POLLIN|POLLHUP; + pfd.revents = 0; + errno = 0, status = poll(&pfd, 1, -1); + } + while (status == -1 && (errno == EINTR || errno == ENOMEM)); + + uv__close(signal_pipe[0]); + uv__close(signal_pipe[1]); + + assert((status == 1) + && "poll() on pipe read end failed"); + assert((pfd.revents & POLLHUP) == POLLHUP + && "no POLLHUP on pipe read end"); +#endif + + process->pid = pid; + + ev_child_init(&process->child_watcher, uv__chld, pid, 0); + ev_child_start(process->loop->ev, &process->child_watcher); + process->child_watcher.data = process; + + if (stdin_pipe[1] >= 0) { + assert(options.stdin_stream); + assert(stdin_pipe[0] >= 0); + uv__close(stdin_pipe[0]); + uv__nonblock(stdin_pipe[1], 1); + uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1], + UV_WRITABLE); + } + + if (stdout_pipe[0] >= 0) { + assert(options.stdout_stream); + assert(stdout_pipe[1] >= 0); + uv__close(stdout_pipe[1]); + uv__nonblock(stdout_pipe[0], 1); + uv__stream_open((uv_stream_t*)options.stdout_stream, stdout_pipe[0], + UV_READABLE); + } + + if (stderr_pipe[0] >= 0) { + assert(options.stderr_stream); + assert(stderr_pipe[1] >= 0); + uv__close(stderr_pipe[1]); + uv__nonblock(stderr_pipe[0], 1); + uv__stream_open((uv_stream_t*)options.stderr_stream, stderr_pipe[0], + UV_READABLE); + } + + return 0; + +error: + uv_err_new(process->loop, errno); + uv__close(stdin_pipe[0]); + uv__close(stdin_pipe[1]); + uv__close(stdout_pipe[0]); + uv__close(stdout_pipe[1]); + uv__close(stderr_pipe[0]); + uv__close(stderr_pipe[1]); + return -1; +} + + +int uv_process_kill(uv_process_t* process, int signum) { + int r = kill(process->pid, signum); + + if (r) { + uv_err_new(process->loop, errno); + return -1; + } else { + return 0; + } +} diff --git a/src/rt/libuv/src/unix/stream.c b/src/rt/libuv/src/unix/stream.c new file mode 100644 index 00000000000..3983ca23b6d --- /dev/null +++ b/src/rt/libuv/src/unix/stream.c @@ -0,0 +1,783 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + + +static void uv__stream_connect(uv_stream_t*); +static void uv__write(uv_stream_t* stream); +static void uv__read(uv_stream_t* stream); + + +static size_t uv__buf_count(uv_buf_t bufs[], int bufcnt) { + size_t total = 0; + int i; + + for (i = 0; i < bufcnt; i++) { + total += bufs[i].len; + } + + return total; +} + + +void uv__stream_init(uv_loop_t* loop, + uv_stream_t* stream, + uv_handle_type type) { + uv__handle_init(loop, (uv_handle_t*)stream, type); + + stream->alloc_cb = NULL; + stream->close_cb = NULL; + stream->connection_cb = NULL; + stream->connect_req = NULL; + stream->accepted_fd = -1; + stream->fd = -1; + stream->delayed_error = 0; + ngx_queue_init(&stream->write_queue); + ngx_queue_init(&stream->write_completed_queue); + stream->write_queue_size = 0; + + ev_init(&stream->read_watcher, uv__stream_io); + stream->read_watcher.data = stream; + + ev_init(&stream->write_watcher, uv__stream_io); + stream->write_watcher.data = stream; + + assert(ngx_queue_empty(&stream->write_queue)); + assert(ngx_queue_empty(&stream->write_completed_queue)); + assert(stream->write_queue_size == 0); +} + + +int uv__stream_open(uv_stream_t* stream, int fd, int flags) { + socklen_t yes; + + assert(fd >= 0); + stream->fd = fd; + + ((uv_handle_t*)stream)->flags |= flags; + + /* Reuse the port address if applicable. */ + yes = 1; + if (stream->type == UV_TCP + && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + uv_err_new(stream->loop, errno); + return -1; + } + + /* Associate the fd with each ev_io watcher. */ + ev_io_set(&stream->read_watcher, fd, EV_READ); + ev_io_set(&stream->write_watcher, fd, EV_WRITE); + + /* These should have been set up by uv_tcp_init or uv_pipe_init. */ + assert(stream->read_watcher.cb == uv__stream_io); + assert(stream->write_watcher.cb == uv__stream_io); + + return 0; +} + + +void uv__stream_destroy(uv_stream_t* stream) { + uv_write_t* req; + ngx_queue_t* q; + + assert(stream->flags & UV_CLOSED); + + while (!ngx_queue_empty(&stream->write_queue)) { + q = ngx_queue_head(&stream->write_queue); + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_write_t, queue); + if (req->bufs != req->bufsml) + free(req->bufs); + + if (req->cb) { + uv_err_new_artificial(req->handle->loop, UV_EINTR); + req->cb(req, -1); + } + } + + while (!ngx_queue_empty(&stream->write_completed_queue)) { + q = ngx_queue_head(&stream->write_completed_queue); + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_write_t, queue); + if (req->cb) { + uv_err_new_artificial(stream->loop, req->error); + req->cb(req, req->error ? -1 : 0); + } + } +} + + +void uv__server_io(EV_P_ ev_io* watcher, int revents) { + int fd; + struct sockaddr_storage addr; + uv_stream_t* stream = watcher->data; + + assert(watcher == &stream->read_watcher || + watcher == &stream->write_watcher); + assert(revents == EV_READ); + + assert(!(stream->flags & UV_CLOSING)); + + if (stream->accepted_fd >= 0) { + ev_io_stop(EV_A, &stream->read_watcher); + return; + } + + /* connection_cb can close the server socket while we're + * in the loop so check it on each iteration. + */ + while (stream->fd != -1) { + assert(stream->accepted_fd < 0); + fd = uv__accept(stream->fd, (struct sockaddr*)&addr, sizeof addr); + + if (fd < 0) { + if (errno == EAGAIN) { + /* No problem. */ + return; + } else if (errno == EMFILE) { + /* TODO special trick. unlock reserved socket, accept, close. */ + return; + } else { + uv_err_new(stream->loop, errno); + stream->connection_cb((uv_stream_t*)stream, -1); + } + } else { + stream->accepted_fd = fd; + stream->connection_cb((uv_stream_t*)stream, 0); + if (stream->accepted_fd >= 0) { + /* The user hasn't yet accepted called uv_accept() */ + ev_io_stop(stream->loop->ev, &stream->read_watcher); + return; + } + } + } +} + + +int uv_accept(uv_stream_t* server, uv_stream_t* client) { + uv_stream_t* streamServer; + uv_stream_t* streamClient; + int saved_errno; + int status; + + /* TODO document this */ + assert(server->loop == client->loop); + + saved_errno = errno; + status = -1; + + streamServer = (uv_stream_t*)server; + streamClient = (uv_stream_t*)client; + + if (streamServer->accepted_fd < 0) { + uv_err_new(server->loop, EAGAIN); + goto out; + } + + if (uv__stream_open(streamClient, streamServer->accepted_fd, + UV_READABLE | UV_WRITABLE)) { + /* TODO handle error */ + streamServer->accepted_fd = -1; + uv__close(streamServer->accepted_fd); + goto out; + } + + ev_io_start(streamServer->loop->ev, &streamServer->read_watcher); + streamServer->accepted_fd = -1; + status = 0; + +out: + errno = saved_errno; + return status; +} + + +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { + switch (stream->type) { + case UV_TCP: + return uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + case UV_NAMED_PIPE: + return uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + default: + assert(0); + return -1; + } +} + + +uv_write_t* uv_write_queue_head(uv_stream_t* stream) { + ngx_queue_t* q; + uv_write_t* req; + + if (ngx_queue_empty(&stream->write_queue)) { + return NULL; + } + + q = ngx_queue_head(&stream->write_queue); + if (!q) { + return NULL; + } + + req = ngx_queue_data(q, struct uv_write_s, queue); + assert(req); + + return req; +} + + +static void uv__drain(uv_stream_t* stream) { + uv_shutdown_t* req; + + assert(!uv_write_queue_head(stream)); + assert(stream->write_queue_size == 0); + + ev_io_stop(stream->loop->ev, &stream->write_watcher); + + /* Shutdown? */ + if ((stream->flags & UV_SHUTTING) && + !(stream->flags & UV_CLOSING) && + !(stream->flags & UV_SHUT)) { + assert(stream->shutdown_req); + + req = stream->shutdown_req; + + if (shutdown(stream->fd, SHUT_WR)) { + /* Error. Report it. User should call uv_close(). */ + uv_err_new(stream->loop, errno); + if (req->cb) { + req->cb(req, -1); + } + } else { + uv_err_new(stream->loop, 0); + ((uv_handle_t*) stream)->flags |= UV_SHUT; + if (req->cb) { + req->cb(req, 0); + } + } + } +} + + +static size_t uv__write_req_size(uv_write_t* req) { + size_t size; + + size = uv__buf_count(req->bufs + req->write_index, + req->bufcnt - req->write_index); + assert(req->handle->write_queue_size >= size); + + return size; +} + + +static void uv__write_req_finish(uv_write_t* req) { + uv_stream_t* stream = req->handle; + + /* Pop the req off tcp->write_queue. */ + ngx_queue_remove(&req->queue); + if (req->bufs != req->bufsml) { + free(req->bufs); + } + req->bufs = NULL; + + /* Add it to the write_completed_queue where it will have its + * callback called in the near future. + */ + ngx_queue_insert_tail(&stream->write_completed_queue, &req->queue); + ev_feed_event(stream->loop->ev, &stream->write_watcher, EV_WRITE); +} + + +/* On success returns NULL. On error returns a pointer to the write request + * which had the error. + */ +static void uv__write(uv_stream_t* stream) { + uv_write_t* req; + struct iovec* iov; + int iovcnt; + ssize_t n; + + assert(stream->fd >= 0); + + /* TODO: should probably while(1) here until EAGAIN */ + + /* Get the request at the head of the queue. */ + req = uv_write_queue_head(stream); + if (!req) { + assert(stream->write_queue_size == 0); + return; + } + + assert(req->handle == stream); + + /* Cast to iovec. We had to have our own uv_buf_t instead of iovec + * because Windows's WSABUF is not an iovec. + */ + assert(sizeof(uv_buf_t) == sizeof(struct iovec)); + iov = (struct iovec*) &(req->bufs[req->write_index]); + iovcnt = req->bufcnt - req->write_index; + + /* Now do the actual writev. Note that we've been updating the pointers + * inside the iov each time we write. So there is no need to offset it. + */ + + do { + if (iovcnt == 1) { + n = write(stream->fd, iov[0].iov_base, iov[0].iov_len); + } else { + n = writev(stream->fd, iov, iovcnt); + } + } + while (n == -1 && errno == EINTR); + + if (n < 0) { + if (errno != EAGAIN) { + /* Error */ + req->error = errno; + stream->write_queue_size -= uv__write_req_size(req); + uv__write_req_finish(req); + return; + } + } else { + /* Successful write */ + + /* Update the counters. */ + while (n >= 0) { + uv_buf_t* buf = &(req->bufs[req->write_index]); + size_t len = buf->len; + + assert(req->write_index < req->bufcnt); + + if ((size_t)n < len) { + buf->base += n; + buf->len -= n; + stream->write_queue_size -= n; + n = 0; + + /* There is more to write. Break and ensure the watcher is pending. */ + break; + + } else { + /* Finished writing the buf at index req->write_index. */ + req->write_index++; + + assert((size_t)n >= len); + n -= len; + + assert(stream->write_queue_size >= len); + stream->write_queue_size -= len; + + if (req->write_index == req->bufcnt) { + /* Then we're done! */ + assert(n == 0); + uv__write_req_finish(req); + /* TODO: start trying to write the next request. */ + return; + } + } + } + } + + /* Either we've counted n down to zero or we've got EAGAIN. */ + assert(n == 0 || n == -1); + + /* We're not done. */ + ev_io_start(stream->loop->ev, &stream->write_watcher); +} + + +static void uv__write_callbacks(uv_stream_t* stream) { + int callbacks_made = 0; + ngx_queue_t* q; + uv_write_t* req; + + while (!ngx_queue_empty(&stream->write_completed_queue)) { + /* Pop a req off write_completed_queue. */ + q = ngx_queue_head(&stream->write_completed_queue); + assert(q); + req = ngx_queue_data(q, struct uv_write_s, queue); + ngx_queue_remove(q); + + /* NOTE: call callback AFTER freeing the request data. */ + if (req->cb) { + uv_err_new_artificial(stream->loop, req->error); + req->cb(req, req->error ? -1 : 0); + } + + callbacks_made++; + } + + assert(ngx_queue_empty(&stream->write_completed_queue)); + + /* Write queue drained. */ + if (!uv_write_queue_head(stream)) { + uv__drain(stream); + } +} + + +static void uv__read(uv_stream_t* stream) { + uv_buf_t buf; + ssize_t nread; + struct ev_loop* ev = stream->loop->ev; + + /* XXX: Maybe instead of having UV_READING we just test if + * tcp->read_cb is NULL or not? + */ + while (stream->read_cb && ((uv_handle_t*)stream)->flags & UV_READING) { + assert(stream->alloc_cb); + buf = stream->alloc_cb((uv_handle_t*)stream, 64 * 1024); + + assert(buf.len > 0); + assert(buf.base); + assert(stream->fd >= 0); + + do { + nread = read(stream->fd, buf.base, buf.len); + } + while (nread < 0 && errno == EINTR); + + if (nread < 0) { + /* Error */ + if (errno == EAGAIN) { + /* Wait for the next one. */ + if (stream->flags & UV_READING) { + ev_io_start(ev, &stream->read_watcher); + } + uv_err_new(stream->loop, EAGAIN); + stream->read_cb(stream, 0, buf); + return; + } else { + /* Error. User should call uv_close(). */ + uv_err_new(stream->loop, errno); + stream->read_cb(stream, -1, buf); + assert(!ev_is_active(&stream->read_watcher)); + return; + } + } else if (nread == 0) { + /* EOF */ + uv_err_new_artificial(stream->loop, UV_EOF); + ev_io_stop(ev, &stream->read_watcher); + stream->read_cb(stream, -1, buf); + return; + } else { + /* Successful read */ + stream->read_cb(stream, nread, buf); + } + } +} + + +int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { + assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE) && + "uv_shutdown (unix) only supports uv_handle_t right now"); + assert(stream->fd >= 0); + + if (!(stream->flags & UV_WRITABLE) || + stream->flags & UV_SHUT || + stream->flags & UV_CLOSED || + stream->flags & UV_CLOSING) { + uv_err_new(stream->loop, EINVAL); + return -1; + } + + /* Initialize request */ + uv__req_init((uv_req_t*)req); + req->handle = stream; + req->cb = cb; + + stream->shutdown_req = req; + req->type = UV_SHUTDOWN; + + ((uv_handle_t*)stream)->flags |= UV_SHUTTING; + + + ev_io_start(stream->loop->ev, &stream->write_watcher); + + return 0; +} + + +void uv__stream_io(EV_P_ ev_io* watcher, int revents) { + uv_stream_t* stream = watcher->data; + + assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || + stream->type == UV_TTY); + assert(watcher == &stream->read_watcher || + watcher == &stream->write_watcher); + assert(!(stream->flags & UV_CLOSING)); + + if (stream->connect_req) { + uv__stream_connect(stream); + } else { + assert(revents & (EV_READ | EV_WRITE)); + assert(stream->fd >= 0); + + if (revents & EV_READ) { + uv__read((uv_stream_t*)stream); + } + + if (revents & EV_WRITE) { + uv__write(stream); + uv__write_callbacks(stream); + } + } +} + + +/** + * We get called here from directly following a call to connect(2). + * In order to determine if we've errored out or succeeded must call + * getsockopt. + */ +static void uv__stream_connect(uv_stream_t* stream) { + int error; + uv_connect_t* req = stream->connect_req; + socklen_t errorsize = sizeof(int); + + assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE); + assert(req); + + if (stream->delayed_error) { + /* To smooth over the differences between unixes errors that + * were reported synchronously on the first connect can be delayed + * until the next tick--which is now. + */ + error = stream->delayed_error; + stream->delayed_error = 0; + } else { + /* Normal situation: we need to get the socket error from the kernel. */ + assert(stream->fd >= 0); + getsockopt(stream->fd, SOL_SOCKET, SO_ERROR, &error, &errorsize); + } + + if (!error) { + ev_io_start(stream->loop->ev, &stream->read_watcher); + + /* Successful connection */ + stream->connect_req = NULL; + if (req->cb) { + req->cb(req, 0); + } + + } else if (error == EINPROGRESS) { + /* Still connecting. */ + return; + } else { + /* Error */ + uv_err_new(stream->loop, error); + + stream->connect_req = NULL; + if (req->cb) { + req->cb(req, -1); + } + } +} + + +int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, + socklen_t addrlen, uv_connect_cb cb) { + int sockfd; + int r; + + if (stream->fd <= 0) { + if ((sockfd = uv__socket(addr->sa_family, SOCK_STREAM, 0)) == -1) { + uv_err_new(stream->loop, errno); + return -1; + } + + if (uv__stream_open(stream, sockfd, UV_READABLE | UV_WRITABLE)) { + uv__close(sockfd); + return -2; + } + } + + uv__req_init((uv_req_t*)req); + req->cb = cb; + req->handle = stream; + req->type = UV_CONNECT; + ngx_queue_init(&req->queue); + + if (stream->connect_req) { + uv_err_new(stream->loop, EALREADY); + return -1; + } + + if (stream->type != UV_TCP) { + uv_err_new(stream->loop, ENOTSOCK); + return -1; + } + + stream->connect_req = req; + + do { + r = connect(stream->fd, addr, addrlen); + } + while (r == -1 && errno == EINTR); + + stream->delayed_error = 0; + + if (r != 0 && errno != EINPROGRESS) { + switch (errno) { + /* If we get a ECONNREFUSED wait until the next tick to report the + * error. Solaris wants to report immediately--other unixes want to + * wait. + */ + case ECONNREFUSED: + stream->delayed_error = errno; + break; + + default: + uv_err_new(stream->loop, errno); + return -1; + } + } + + assert(stream->write_watcher.data == stream); + ev_io_start(stream->loop->ev, &stream->write_watcher); + + if (stream->delayed_error) { + ev_feed_event(stream->loop->ev, &stream->write_watcher, EV_WRITE); + } + + return 0; +} + + +/* The buffers to be written must remain valid until the callback is called. + * This is not required for the uv_buf_t array. + */ +int uv_write(uv_write_t* req, uv_stream_t* stream, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb) { + int empty_queue; + + assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || + stream->type == UV_TTY) && + "uv_write (unix) does not yet support other types of streams"); + + if (stream->fd < 0) { + uv_err_new(stream->loop, EBADF); + return -1; + } + + empty_queue = (stream->write_queue_size == 0); + + /* Initialize the req */ + uv__req_init((uv_req_t*) req); + req->cb = cb; + req->handle = stream; + req->error = 0; + req->type = UV_WRITE; + ngx_queue_init(&req->queue); + + if (bufcnt <= UV_REQ_BUFSML_SIZE) { + req->bufs = req->bufsml; + } + else { + req->bufs = malloc(sizeof(uv_buf_t) * bufcnt); + } + + memcpy(req->bufs, bufs, bufcnt * sizeof(uv_buf_t)); + req->bufcnt = bufcnt; + + /* + * fprintf(stderr, "cnt: %d bufs: %p bufsml: %p\n", bufcnt, req->bufs, req->bufsml); + */ + + req->write_index = 0; + stream->write_queue_size += uv__buf_count(bufs, bufcnt); + + /* Append the request to write_queue. */ + ngx_queue_insert_tail(&stream->write_queue, &req->queue); + + assert(!ngx_queue_empty(&stream->write_queue)); + assert(stream->write_watcher.cb == uv__stream_io); + assert(stream->write_watcher.data == stream); + assert(stream->write_watcher.fd == stream->fd); + + /* If the queue was empty when this function began, we should attempt to + * do the write immediately. Otherwise start the write_watcher and wait + * for the fd to become writable. + */ + if (empty_queue) { + uv__write(stream); + } else { + ev_io_start(stream->loop->ev, &stream->write_watcher); + } + + return 0; +} + + +int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { + assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || + stream->type == UV_TTY); + + if (stream->flags & UV_CLOSING) { + uv_err_new(stream->loop, EINVAL); + return -1; + } + + /* The UV_READING flag is irrelevant of the state of the tcp - it just + * expresses the desired state of the user. + */ + ((uv_handle_t*)stream)->flags |= UV_READING; + + /* TODO: try to do the read inline? */ + /* TODO: keep track of tcp state. If we've gotten a EOF then we should + * not start the IO watcher. + */ + assert(stream->fd >= 0); + assert(alloc_cb); + + stream->read_cb = read_cb; + stream->alloc_cb = alloc_cb; + + /* These should have been set by uv_tcp_init. */ + assert(stream->read_watcher.cb == uv__stream_io); + + ev_io_start(stream->loop->ev, &stream->read_watcher); + return 0; +} + + +int uv_read_stop(uv_stream_t* stream) { + uv_tcp_t* tcp = (uv_tcp_t*)stream; + + ((uv_handle_t*)tcp)->flags &= ~UV_READING; + + ev_io_stop(tcp->loop->ev, &tcp->read_watcher); + tcp->read_cb = NULL; + tcp->alloc_cb = NULL; + return 0; +} + + diff --git a/src/rt/libuv/src/uv-sunos.c b/src/rt/libuv/src/unix/sunos.c similarity index 84% rename from src/rt/libuv/src/uv-sunos.c rename to src/rt/libuv/src/unix/sunos.c index 4a75461413e..0b5c03b5a69 100644 --- a/src/rt/libuv/src/uv-sunos.c +++ b/src/rt/libuv/src/unix/sunos.c @@ -22,8 +22,11 @@ #include #include -#include +#include +#include + #include +#include uint64_t uv_hrtime() { @@ -37,7 +40,7 @@ uint64_t uv_hrtime() { * we don't want to potentially create a race condition in the use of snprintf. */ int uv_exepath(char* buffer, size_t* size) { - size_t res; + ssize_t res; pid_t pid; char buf[128]; @@ -58,3 +61,17 @@ int uv_exepath(char* buffer, size_t* size) { *size = res; return (0); } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/tcp.c b/src/rt/libuv/src/unix/tcp.c new file mode 100644 index 00000000000..9f14c1b6c0f --- /dev/null +++ b/src/rt/libuv/src/unix/tcp.c @@ -0,0 +1,280 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + + +int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { + uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); + loop->counters.tcp_init++; + return 0; +} + + +static int uv__tcp_bind(uv_tcp_t* tcp, + int domain, + struct sockaddr* addr, + int addrsize) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (tcp->fd < 0) { + if ((tcp->fd = uv__socket(domain, SOCK_STREAM, 0)) == -1) { + uv_err_new(tcp->loop, errno); + goto out; + } + + if (uv__stream_open((uv_stream_t*)tcp, tcp->fd, UV_READABLE | UV_WRITABLE)) { + uv__close(tcp->fd); + tcp->fd = -1; + status = -2; + goto out; + } + } + + assert(tcp->fd >= 0); + + tcp->delayed_error = 0; + if (bind(tcp->fd, addr, addrsize) == -1) { + if (errno == EADDRINUSE) { + tcp->delayed_error = errno; + } else { + uv_err_new(tcp->loop, errno); + goto out; + } + } + status = 0; + +out: + errno = saved_errno; + return status; +} + + +int uv_tcp_bind(uv_tcp_t* tcp, struct sockaddr_in addr) { + if (addr.sin_family != AF_INET) { + uv_err_new(tcp->loop, EFAULT); + return -1; + } + + return uv__tcp_bind(tcp, + AF_INET, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)); +} + + +int uv_tcp_bind6(uv_tcp_t* tcp, struct sockaddr_in6 addr) { + if (addr.sin6_family != AF_INET6) { + uv_err_new(tcp->loop, EFAULT); + return -1; + } + + return uv__tcp_bind(tcp, + AF_INET6, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in6)); +} + + +int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + socklen_t socklen; + int saved_errno; + int rv = 0; + + /* Don't clobber errno. */ + saved_errno = errno; + + if (handle->delayed_error) { + uv_err_new(handle->loop, handle->delayed_error); + rv = -1; + goto out; + } + + if (handle->fd < 0) { + uv_err_new(handle->loop, EINVAL); + rv = -1; + goto out; + } + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t)*namelen; + + if (getsockname(handle->fd, name, &socklen) == -1) { + uv_err_new(handle->loop, errno); + rv = -1; + } else { + *namelen = (int)socklen; + } + +out: + errno = saved_errno; + return rv; +} + + +int uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + socklen_t socklen; + int saved_errno; + int rv = 0; + + /* Don't clobber errno. */ + saved_errno = errno; + + if (handle->delayed_error) { + uv_err_new(handle->loop, handle->delayed_error); + rv = -1; + goto out; + } + + if (handle->fd < 0) { + uv_err_new(handle->loop, EINVAL); + rv = -1; + goto out; + } + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t)*namelen; + + if (getpeername(handle->fd, name, &socklen) == -1) { + uv_err_new(handle->loop, errno); + rv = -1; + } else { + *namelen = (int)socklen; + } + +out: + errno = saved_errno; + return rv; +} + + +int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { + int r; + + if (tcp->delayed_error) { + uv_err_new(tcp->loop, tcp->delayed_error); + return -1; + } + + if (tcp->fd < 0) { + if ((tcp->fd = uv__socket(AF_INET, SOCK_STREAM, 0)) == -1) { + uv_err_new(tcp->loop, errno); + return -1; + } + + if (uv__stream_open((uv_stream_t*)tcp, tcp->fd, UV_READABLE)) { + uv__close(tcp->fd); + tcp->fd = -1; + return -1; + } + } + + assert(tcp->fd >= 0); + + r = listen(tcp->fd, backlog); + if (r < 0) { + uv_err_new(tcp->loop, errno); + return -1; + } + + tcp->connection_cb = cb; + + /* Start listening for connections. */ + ev_io_set(&tcp->read_watcher, tcp->fd, EV_READ); + ev_set_cb(&tcp->read_watcher, uv__server_io); + ev_io_start(tcp->loop->ev, &tcp->read_watcher); + + return 0; +} + + +int uv_tcp_connect(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr_in address, + uv_connect_cb cb) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->type != UV_TCP) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + if (address.sin_family != AF_INET) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + status = uv__connect(req, + (uv_stream_t*)handle, + (struct sockaddr*)&address, + sizeof address, + cb); + +out: + errno = saved_errno; + return status; +} + + +int uv_tcp_connect6(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr_in6 address, + uv_connect_cb cb) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->type != UV_TCP) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + if (address.sin6_family != AF_INET6) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + status = uv__connect(req, + (uv_stream_t*)handle, + (struct sockaddr*)&address, + sizeof address, + cb); + +out: + errno = saved_errno; + return status; +} diff --git a/src/rt/libuv/src/unix/tty.c b/src/rt/libuv/src/unix/tty.c new file mode 100644 index 00000000000..989c09d1bf1 --- /dev/null +++ b/src/rt/libuv/src/unix/tty.c @@ -0,0 +1,110 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include + + +int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd) { + uv__nonblock(fd, 1); + uv__stream_init(loop, (uv_stream_t*)tty, UV_TTY); + uv__stream_open((uv_stream_t*)tty, fd, UV_READABLE | UV_WRITABLE); + loop->counters.tty_init++; + return 0; +} + + +int uv_tty_set_mode(uv_tty_t* tty, int mode) { + int fd = tty->fd; + struct termios orig_termios; /* in order to restore at exit */ + struct termios raw; + + if (tcgetattr(fd, &orig_termios) == -1) goto fatal; + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes */ + raw.c_oflag |= (ONLCR); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - echoing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(fd, TCSAFLUSH, &raw) < 0) goto fatal; + return 0; + +fatal: + uv_err_new(tty->loop, ENOTTY); + return -1; +} + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + struct winsize ws; + + if (ioctl(tty->fd, TIOCGWINSZ, &ws) < 0) { + uv_err_new(tty->loop, errno); + return -1; + } + + *width = ws.ws_col; + *height = ws.ws_row; + + return 0; +} + + +uv_handle_type uv_guess_handle(uv_file file) { + struct stat s; + + if (file < 0) { + uv_err_new(NULL, EINVAL); /* XXX Need loop? */ + return -1; + } + + if (isatty(file)) { + return UV_TTY; + } + + if (fstat(file, &s)) { + uv_err_new(NULL, errno); /* XXX Need loop? */ + return -1; + } + + if (!S_ISSOCK(s.st_mode) && !S_ISFIFO(s.st_mode)) { + return UV_FILE; + } + + return UV_NAMED_PIPE; +} diff --git a/src/rt/libuv/src/unix/udp.c b/src/rt/libuv/src/unix/udp.c new file mode 100644 index 00000000000..20e5f3798b9 --- /dev/null +++ b/src/rt/libuv/src/unix/udp.c @@ -0,0 +1,555 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include + + +static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w); +static void uv__udp_run_completed(uv_udp_t* handle); +static void uv__udp_run_pending(uv_udp_t* handle); +static void uv__udp_recvmsg(uv_udp_t* handle); +static void uv__udp_sendmsg(uv_udp_t* handle); +static void uv__udp_io(EV_P_ ev_io* w, int events); +static int uv__udp_bind(uv_udp_t* handle, int domain, struct sockaddr* addr, + socklen_t len, unsigned flags); +static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain); +static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr* addr, socklen_t addrlen, uv_udp_send_cb send_cb); + + +static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w) { + int flags; + + assert(w == &handle->read_watcher + || w == &handle->write_watcher); + + flags = (w == &handle->read_watcher ? EV_READ : EV_WRITE); + + w->data = handle; + ev_set_cb(w, uv__udp_io); + ev_io_set(w, handle->fd, flags); + ev_io_start(handle->loop->ev, w); +} + + +void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w) { + int flags; + + assert(w == &handle->read_watcher + || w == &handle->write_watcher); + + flags = (w == &handle->read_watcher ? EV_READ : EV_WRITE); + + ev_io_stop(handle->loop->ev, w); + ev_io_set(w, -1, flags); + ev_set_cb(w, NULL); + w->data = (void*)0xDEADBABE; +} + + +void uv__udp_destroy(uv_udp_t* handle) { + uv_udp_send_t* req; + ngx_queue_t* q; + + uv__udp_run_completed(handle); + + while (!ngx_queue_empty(&handle->write_queue)) { + q = ngx_queue_head(&handle->write_queue); + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_udp_send_t, queue); + if (req->send_cb) { + /* FIXME proper error code like UV_EABORTED */ + uv_err_new_artificial(handle->loop, UV_EINTR); + req->send_cb(req, -1); + } + } + + /* Now tear down the handle. */ + handle->flags = 0; + handle->recv_cb = NULL; + handle->alloc_cb = NULL; + /* but _do not_ touch close_cb */ + + if (handle->fd != -1) { + uv__close(handle->fd); + handle->fd = -1; + } + + uv__udp_watcher_stop(handle, &handle->read_watcher); + uv__udp_watcher_stop(handle, &handle->write_watcher); +} + + +static void uv__udp_run_pending(uv_udp_t* handle) { + uv_udp_send_t* req; + ngx_queue_t* q; + struct msghdr h; + ssize_t size; + + while (!ngx_queue_empty(&handle->write_queue)) { + q = ngx_queue_head(&handle->write_queue); + assert(q != NULL); + + req = ngx_queue_data(q, uv_udp_send_t, queue); + assert(req != NULL); + + memset(&h, 0, sizeof h); + h.msg_name = &req->addr; + h.msg_namelen = req->addrlen; + h.msg_iov = (struct iovec*)req->bufs; + h.msg_iovlen = req->bufcnt; + + do { + size = sendmsg(handle->fd, &h, 0); + } + while (size == -1 && errno == EINTR); + + /* TODO try to write once or twice more in the + * hope that the socket becomes readable again? + */ + if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + break; + + req->status = (size == -1 ? -errno : size); + +#ifndef NDEBUG + /* Sanity check. */ + if (size != -1) { + ssize_t nbytes; + int i; + + for (nbytes = i = 0; i < req->bufcnt; i++) + nbytes += req->bufs[i].len; + + assert(size == nbytes); + } +#endif + + /* Sending a datagram is an atomic operation: either all data + * is written or nothing is (and EMSGSIZE is raised). That is + * why we don't handle partial writes. Just pop the request + * off the write queue and onto the completed queue, done. + */ + ngx_queue_remove(&req->queue); + ngx_queue_insert_tail(&handle->write_completed_queue, &req->queue); + } +} + + +static void uv__udp_run_completed(uv_udp_t* handle) { + uv_udp_send_t* req; + ngx_queue_t* q; + + while (!ngx_queue_empty(&handle->write_completed_queue)) { + q = ngx_queue_head(&handle->write_completed_queue); + assert(q != NULL); + + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_udp_send_t, queue); + assert(req != NULL); + + if (req->bufs != req->bufsml) + free(req->bufs); + + if (req->send_cb == NULL) + continue; + + /* req->status >= 0 == bytes written + * req->status < 0 == errno + */ + if (req->status >= 0) { + req->send_cb(req, 0); + } + else { + uv_err_new(handle->loop, -req->status); + req->send_cb(req, -1); + } + } +} + + +static void uv__udp_recvmsg(uv_udp_t* handle) { + struct sockaddr_storage peer; + struct msghdr h; + ssize_t nread; + uv_buf_t buf; + int flags; + + assert(handle->recv_cb != NULL); + assert(handle->alloc_cb != NULL); + + do { + /* FIXME: hoist alloc_cb out the loop but for now follow uv__read() */ + buf = handle->alloc_cb((uv_handle_t*)handle, 64 * 1024); + assert(buf.len > 0); + assert(buf.base != NULL); + + memset(&h, 0, sizeof h); + h.msg_name = &peer; + h.msg_namelen = sizeof peer; + h.msg_iov = (struct iovec*)&buf; + h.msg_iovlen = 1; + + do { + nread = recvmsg(handle->fd, &h, 0); + } + while (nread == -1 && errno == EINTR); + + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + uv_err_new(handle->loop, EAGAIN); + handle->recv_cb(handle, 0, buf, NULL, 0); + } + else { + uv_err_new(handle->loop, errno); + handle->recv_cb(handle, -1, buf, NULL, 0); + } + } + else { + flags = 0; + + if (h.msg_flags & MSG_TRUNC) + flags |= UV_UDP_PARTIAL; + + handle->recv_cb(handle, + nread, + buf, + (struct sockaddr*)&peer, + flags); + } + } + /* recv_cb callback may decide to pause or close the handle */ + while (nread != -1 + && handle->fd != -1 + && handle->recv_cb != NULL); +} + + +static void uv__udp_sendmsg(uv_udp_t* handle) { + assert(!ngx_queue_empty(&handle->write_queue) + || !ngx_queue_empty(&handle->write_completed_queue)); + + /* Write out pending data first. */ + uv__udp_run_pending(handle); + + /* Drain 'request completed' queue. */ + uv__udp_run_completed(handle); + + if (!ngx_queue_empty(&handle->write_completed_queue)) { + /* Schedule completion callbacks. */ + ev_feed_event(handle->loop->ev, &handle->write_watcher, EV_WRITE); + } + else if (ngx_queue_empty(&handle->write_queue)) { + /* Pending queue and completion queue empty, stop watcher. */ + uv__udp_watcher_stop(handle, &handle->write_watcher); + } +} + + +static void uv__udp_io(EV_P_ ev_io* w, int events) { + uv_udp_t* handle; + + handle = w->data; + assert(handle != NULL); + assert(handle->type == UV_UDP); + assert(handle->fd >= 0); + assert(!(events & ~(EV_READ|EV_WRITE))); + + if (events & EV_READ) + uv__udp_recvmsg(handle); + + if (events & EV_WRITE) + uv__udp_sendmsg(handle); +} + + +static int uv__udp_bind(uv_udp_t* handle, + int domain, + struct sockaddr* addr, + socklen_t len, + unsigned flags) { + int saved_errno; + int status; + int yes; + int fd; + + saved_errno = errno; + status = -1; + + /* Check for bad flags. */ + if (flags & ~UV_UDP_IPV6ONLY) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + /* Cannot set IPv6-only mode on non-IPv6 socket. */ + if ((flags & UV_UDP_IPV6ONLY) && domain != AF_INET6) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + /* Check for already active socket. */ + if (handle->fd != -1) { + uv_err_new_artificial(handle->loop, UV_EALREADY); + goto out; + } + + if ((fd = uv__socket(domain, SOCK_DGRAM, 0)) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + if (flags & UV_UDP_IPV6ONLY) { +#ifdef IPV6_V6ONLY + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } +#else + uv_err_new((uv_handle_t*)handle, ENOTSUP); + goto out; +#endif + } + + if (bind(fd, addr, len) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + handle->fd = fd; + status = 0; + +out: + if (status) + uv__close(fd); + + errno = saved_errno; + return status; +} + + +static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain) { + struct sockaddr_storage taddr; + socklen_t addrlen; + + assert(domain == AF_INET || domain == AF_INET6); + + if (handle->fd != -1) + return 0; + + switch (domain) { + case AF_INET: + { + struct sockaddr_in* addr = (void*)&taddr; + memset(addr, 0, sizeof *addr); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = INADDR_ANY; + addrlen = sizeof *addr; + break; + } + case AF_INET6: + { + struct sockaddr_in6* addr = (void*)&taddr; + memset(addr, 0, sizeof *addr); + addr->sin6_family = AF_INET6; + addr->sin6_addr = in6addr_any; + addrlen = sizeof *addr; + break; + } + default: + assert(0 && "unsupported address family"); + abort(); + } + + return uv__udp_bind(handle, domain, (struct sockaddr*)&taddr, addrlen, 0); +} + + +static int uv__udp_send(uv_udp_send_t* req, + uv_udp_t* handle, + uv_buf_t bufs[], + int bufcnt, + struct sockaddr* addr, + socklen_t addrlen, + uv_udp_send_cb send_cb) { + if (uv__udp_maybe_deferred_bind(handle, addr->sa_family)) + return -1; + + /* Don't use uv__req_init(), it zeroes the data field. */ + handle->loop->counters.req_init++; + + memcpy(&req->addr, addr, addrlen); + req->addrlen = addrlen; + req->send_cb = send_cb; + req->handle = handle; + req->bufcnt = bufcnt; + req->type = UV_UDP_SEND; + + if (bufcnt <= UV_REQ_BUFSML_SIZE) { + req->bufs = req->bufsml; + } + else if ((req->bufs = malloc(bufcnt * sizeof(bufs[0]))) == NULL) { + uv_err_new(handle->loop, ENOMEM); + return -1; + } + memcpy(req->bufs, bufs, bufcnt * sizeof(bufs[0])); + + ngx_queue_insert_tail(&handle->write_queue, &req->queue); + uv__udp_watcher_start(handle, &handle->write_watcher); + + return 0; +} + + +int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { + memset(handle, 0, sizeof *handle); + + uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP); + loop->counters.udp_init++; + + handle->fd = -1; + ngx_queue_init(&handle->write_queue); + ngx_queue_init(&handle->write_completed_queue); + + return 0; +} + + +int uv_udp_bind(uv_udp_t* handle, struct sockaddr_in addr, unsigned flags) { + return uv__udp_bind(handle, + AF_INET, + (struct sockaddr*)&addr, + sizeof addr, + flags); +} + + +int uv_udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags) { + return uv__udp_bind(handle, + AF_INET6, + (struct sockaddr*)&addr, + sizeof addr, + flags); +} + + +int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, + int* namelen) { + socklen_t socklen; + int saved_errno; + int rv = 0; + + /* Don't clobber errno. */ + saved_errno = errno; + + if (handle->fd < 0) { + uv_err_new(handle->loop, EINVAL); + rv = -1; + goto out; + } + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t)*namelen; + + if (getsockname(handle->fd, name, &socklen) == -1) { + uv_err_new(handle->loop, errno); + rv = -1; + } else { + *namelen = (int)socklen; + } + +out: + errno = saved_errno; + return rv; +} + + +int uv_udp_send(uv_udp_send_t* req, + uv_udp_t* handle, + uv_buf_t bufs[], + int bufcnt, + struct sockaddr_in addr, + uv_udp_send_cb send_cb) { + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*)&addr, + sizeof addr, + send_cb); +} + + +int uv_udp_send6(uv_udp_send_t* req, + uv_udp_t* handle, + uv_buf_t bufs[], + int bufcnt, + struct sockaddr_in6 addr, + uv_udp_send_cb send_cb) { + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*)&addr, + sizeof addr, + send_cb); +} + + +int uv_udp_recv_start(uv_udp_t* handle, + uv_alloc_cb alloc_cb, + uv_udp_recv_cb recv_cb) { + if (alloc_cb == NULL || recv_cb == NULL) { + uv_err_new_artificial(handle->loop, UV_EINVAL); + return -1; + } + + if (ev_is_active(&handle->read_watcher)) { + uv_err_new_artificial(handle->loop, UV_EALREADY); + return -1; + } + + if (uv__udp_maybe_deferred_bind(handle, AF_INET)) + return -1; + + handle->alloc_cb = alloc_cb; + handle->recv_cb = recv_cb; + uv__udp_watcher_start(handle, &handle->read_watcher); + + return 0; +} + + +int uv_udp_recv_stop(uv_udp_t* handle) { + uv__udp_watcher_stop(handle, &handle->read_watcher); + handle->alloc_cb = NULL; + handle->recv_cb = NULL; + return 0; +} diff --git a/src/rt/libuv/src/uv-eio.c b/src/rt/libuv/src/unix/uv-eio.c similarity index 59% rename from src/rt/libuv/src/uv-eio.c rename to src/rt/libuv/src/unix/uv-eio.c index b07735cad8a..84afe09b747 100644 --- a/src/rt/libuv/src/uv-eio.c +++ b/src/rt/libuv/src/unix/uv-eio.c @@ -22,51 +22,55 @@ #include "uv.h" #include "eio.h" + #include +#include -static uv_async_t uv_eio_want_poll_notifier; -static uv_async_t uv_eio_done_poll_notifier; -static uv_idle_t uv_eio_poller; -static int uv_eio_init_count; +/* TODO remove me! */ +static uv_loop_t* main_loop; static void uv_eio_do_poll(uv_idle_t* watcher, int status) { - assert(watcher == &uv_eio_poller); + assert(watcher == &(watcher->loop->uv_eio_poller)); /* printf("uv_eio_poller\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &uv_eio_poller)) { + if (eio_poll() != -1 && uv_is_active((uv_handle_t*) watcher)) { /* printf("uv_eio_poller stop\n"); */ - uv_idle_stop(&uv_eio_poller); - uv_unref(); + uv_idle_stop(watcher); + uv_unref(watcher->loop); } } /* Called from the main thread. */ static void uv_eio_want_poll_notifier_cb(uv_async_t* watcher, int status) { - assert(watcher == &uv_eio_want_poll_notifier); + uv_loop_t* loop = watcher->loop; + + assert(watcher == &loop->uv_eio_want_poll_notifier); /* printf("want poll notifier\n"); */ - if (eio_poll() == -1 && !uv_is_active((uv_handle_t*) &uv_eio_poller)) { + if (eio_poll() == -1 && !uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller start\n"); */ - uv_idle_start(&uv_eio_poller, uv_eio_do_poll); - uv_ref(); + uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); + uv_ref(loop); } } static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { - assert(watcher == &uv_eio_done_poll_notifier); + uv_loop_t* loop = watcher->loop; + + assert(watcher == &loop->uv_eio_done_poll_notifier); /* printf("done poll notifier\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &uv_eio_poller)) { + if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller stop\n"); */ - uv_idle_stop(&uv_eio_poller); - uv_unref(); + uv_idle_stop(&loop->uv_eio_poller); + uv_unref(loop); } } @@ -77,7 +81,13 @@ static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { */ static void uv_eio_want_poll(void) { /* Signal the main thread that eio_poll need to be processed. */ - uv_async_send(&uv_eio_want_poll_notifier); + + /* + * TODO need to select the correct uv_loop_t and async_send to + * uv_eio_want_poll_notifier. + */ + + uv_async_send(&main_loop->uv_eio_want_poll_notifier); } @@ -86,22 +96,27 @@ static void uv_eio_done_poll(void) { * Signal the main thread that we should stop calling eio_poll(). * from the idle watcher. */ - uv_async_send(&uv_eio_done_poll_notifier); + uv_async_send(&main_loop->uv_eio_done_poll_notifier); } -void uv_eio_init() { - if (uv_eio_init_count == 0) { - uv_eio_init_count++; +void uv_eio_init(uv_loop_t* loop) { + if (loop->counters.eio_init == 0) { + loop->counters.eio_init++; - uv_idle_init(&uv_eio_poller); - uv_idle_start(&uv_eio_poller, uv_eio_do_poll); + main_loop = loop; - uv_async_init(&uv_eio_want_poll_notifier, uv_eio_want_poll_notifier_cb); - uv_unref(); + uv_idle_init(loop, &loop->uv_eio_poller); + uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); - uv_async_init(&uv_eio_done_poll_notifier, uv_eio_done_poll_notifier_cb); - uv_unref(); + loop->uv_eio_want_poll_notifier.data = loop; + uv_async_init(loop, &loop->uv_eio_want_poll_notifier, + uv_eio_want_poll_notifier_cb); + uv_unref(loop); + + uv_async_init(loop, &loop->uv_eio_done_poll_notifier, + uv_eio_done_poll_notifier_cb); + uv_unref(loop); eio_init(uv_eio_want_poll, uv_eio_done_poll); /* @@ -109,5 +124,11 @@ void uv_eio_init() { * race conditions. See Node's test/simple/test-eio-race.js */ eio_set_max_poll_reqs(10); + } else { + /* + * If this assertion breaks then Ryan hasn't implemented support for + * receiving thread pool requests back to multiple threads. + */ + assert(main_loop == loop); } } diff --git a/src/rt/libuv/src/uv-eio.h b/src/rt/libuv/src/unix/uv-eio.h similarity index 88% rename from src/rt/libuv/src/uv-eio.h rename to src/rt/libuv/src/unix/uv-eio.h index 2f2d3486bf3..711d0cf281c 100644 --- a/src/rt/libuv/src/uv-eio.h +++ b/src/rt/libuv/src/unix/uv-eio.h @@ -9,5 +9,5 @@ * safe to call more than once. * TODO: uv_eio_deinit */ -void uv_eio_init(void); +void uv_eio_init(uv_loop_t*); #endif diff --git a/src/rt/libuv/src/uv-common.c b/src/rt/libuv/src/uv-common.c index 2c4e0cc7659..51188337bd9 100644 --- a/src/rt/libuv/src/uv-common.c +++ b/src/rt/libuv/src/uv-common.c @@ -29,9 +29,7 @@ /* use inet_pton from c-ares if necessary */ #include "ares_config.h" #include "ares/inet_net_pton.h" - -/* list used for ares task handles */ -static uv_ares_task_t* uv_ares_handles_ = NULL; +#include "ares/inet_ntop.h" static uv_counters_t counters; @@ -42,6 +40,14 @@ uv_counters_t* uv_counters() { } +uv_buf_t uv_buf_init(char* base, size_t len) { + uv_buf_t buf; + buf.base = base; + buf.len = len; + return buf; +} + + const char* uv_err_name(uv_err_t err) { switch (err.code) { case UV_UNKNOWN: return "UNKNOWN"; @@ -75,10 +81,13 @@ const char* uv_err_name(uv_err_t err) { case UV_ENOTCONN: return "ENOTCONN"; case UV_ENOTSOCK: return "ENOTSOCK"; case UV_ENOTSUP: return "ENOTSUP"; + case UV_ENOENT: return "ENOENT"; + case UV_EPIPE: return "EPIPE"; case UV_EPROTO: return "EPROTO"; case UV_EPROTONOSUPPORT: return "EPROTONOSUPPORT"; case UV_EPROTOTYPE: return "EPROTOTYPE"; case UV_ETIMEDOUT: return "ETIMEDOUT"; + case UV_EEXIST: return "EEXIST"; default: assert(0); return NULL; @@ -112,21 +121,36 @@ struct sockaddr_in6 uv_ip6_addr(const char* ip, int port) { } +int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size) { + const char* d = ares_inet_ntop(AF_INET, &src->sin_addr, dst, size); + return d != dst; +} + + +int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size) { + const char* d = ares_inet_ntop(AF_INET6, &src->sin6_addr, dst, size); + return d != dst; +} + + /* find matching ares handle in list */ -void uv_add_ares_handle(uv_ares_task_t* handle) { - handle->ares_next = uv_ares_handles_; +void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle) { + handle->loop = loop; + handle->ares_next = loop->uv_ares_handles_; handle->ares_prev = NULL; - if (uv_ares_handles_) { - uv_ares_handles_->ares_prev = handle; + if (loop->uv_ares_handles_) { + loop->uv_ares_handles_->ares_prev = handle; } - uv_ares_handles_ = handle; + + loop->uv_ares_handles_ = handle; } /* find matching ares handle in list */ /* TODO: faster lookup */ -uv_ares_task_t* uv_find_ares_handle(ares_socket_t sock) { - uv_ares_task_t* handle = uv_ares_handles_; +uv_ares_task_t* uv_find_ares_handle(uv_loop_t* loop, ares_socket_t sock) { + uv_ares_task_t* handle = loop->uv_ares_handles_; + while (handle != NULL) { if (handle->sock == sock) { break; @@ -139,8 +163,10 @@ uv_ares_task_t* uv_find_ares_handle(ares_socket_t sock) { /* remove ares handle in list */ void uv_remove_ares_handle(uv_ares_task_t* handle) { - if (handle == uv_ares_handles_) { - uv_ares_handles_ = handle->ares_next; + uv_loop_t* loop = handle->loop; + + if (handle == loop->uv_ares_handles_) { + loop->uv_ares_handles_ = handle->ares_next; } if (handle->ares_next) { @@ -154,6 +180,6 @@ void uv_remove_ares_handle(uv_ares_task_t* handle) { /* Returns 1 if the uv_ares_handles_ list is empty. 0 otherwise. */ -int uv_ares_handles_empty() { - return uv_ares_handles_ ? 0 : 1; +int uv_ares_handles_empty(uv_loop_t* loop) { + return loop->uv_ares_handles_ ? 0 : 1; } diff --git a/src/rt/libuv/src/uv-common.h b/src/rt/libuv/src/uv-common.h index 74b4c4e38bb..ae543195569 100644 --- a/src/rt/libuv/src/uv-common.h +++ b/src/rt/libuv/src/uv-common.h @@ -29,10 +29,8 @@ #include "uv.h" -/* - * Subclass of uv_handle_t. Used for integration of c-ares. - */ -typedef struct uv_ares_task_s uv_ares_task_t; +#define COUNTOF(a) (sizeof(a) / sizeof(a[0])) + struct uv_ares_task_s { UV_HANDLE_FIELDS @@ -43,9 +41,12 @@ struct uv_ares_task_s { void uv_remove_ares_handle(uv_ares_task_t* handle); -uv_ares_task_t* uv_find_ares_handle(ares_socket_t sock); -void uv_add_ares_handle(uv_ares_task_t* handle); -int uv_ares_handles_empty(); +uv_ares_task_t* uv_find_ares_handle(uv_loop_t*, ares_socket_t sock); + +/* TODO Rename to uv_ares_task_init? */ +void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle); + +int uv_ares_handles_empty(uv_loop_t* loop); #endif /* UV_COMMON_H_ */ diff --git a/src/rt/libuv/src/uv-unix.c b/src/rt/libuv/src/uv-unix.c deleted file mode 100644 index e29c842bfae..00000000000 --- a/src/rt/libuv/src/uv-unix.c +++ /dev/null @@ -1,1596 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "uv.h" -#include "uv-common.h" -#include "uv-eio.h" - -#include /* NULL */ -#include /* printf */ -#include -#include /* strerror */ -#include -#include -#include -#include -#include -#include -#include -#include /* PATH_MAX */ - -#if defined(__APPLE__) -#include /* _NSGetExecutablePath */ -#endif - -#if defined(__FreeBSD__) -#include -#endif - - -static uv_err_t last_err; - -struct uv_ares_data_s { - ares_channel channel; - /* - * While the channel is active this timer is called once per second to be sure - * that we're always calling ares_process. See the warning above the - * definition of ares_timeout(). - */ - ev_timer timer; -}; - -static struct uv_ares_data_s ares_data; - - -void uv__tcp_io(EV_P_ ev_io* watcher, int revents); -void uv__next(EV_P_ ev_idle* watcher, int revents); -static void uv__tcp_connect(uv_tcp_t*); -int uv_tcp_open(uv_tcp_t*, int fd); -static void uv__finish_close(uv_handle_t* handle); - -/* flags */ -enum { - UV_CLOSING = 0x00000001, /* uv_close() called but not finished. */ - UV_CLOSED = 0x00000002, /* close(2) finished. */ - UV_READING = 0x00000004, /* uv_read_start() called. */ - UV_SHUTTING = 0x00000008, /* uv_shutdown() called but not complete. */ - UV_SHUT = 0x00000010 /* Write side closed. */ -}; - - -void uv_flag_set(uv_handle_t* handle, int flag) { - handle->flags |= flag; -} - - -/* TODO Share this code with Windows. */ -/* TODO Expose callback to user to handle fatal error like V8 does. */ -static void uv_fatal_error(const int errorno, const char* syscall) { - char* buf = NULL; - const char* errmsg; - - if (buf) { - errmsg = buf; - } else { - errmsg = "Unknown error"; - } - - if (syscall) { - fprintf(stderr, "\nlibuv fatal error. %s: (%d) %s\n", syscall, errorno, - errmsg); - } else { - fprintf(stderr, "\nlibuv fatal error. (%d) %s\n", errorno, errmsg); - } - - abort(); -} - - -uv_err_t uv_last_error() { - return last_err; -} - - -char* uv_strerror(uv_err_t err) { - return strerror(err.sys_errno_); -} - - -void uv_flag_unset(uv_handle_t* handle, int flag) { - handle->flags = handle->flags & ~flag; -} - - -int uv_flag_is_set(uv_handle_t* handle, int flag) { - return (handle->flags & flag) != 0; -} - - -static uv_err_code uv_translate_sys_error(int sys_errno) { - switch (sys_errno) { - case 0: return UV_OK; - case EACCES: return UV_EACCESS; - case EAGAIN: return UV_EAGAIN; - case ECONNRESET: return UV_ECONNRESET; - case EFAULT: return UV_EFAULT; - case EMFILE: return UV_EMFILE; - case EINVAL: return UV_EINVAL; - case ECONNREFUSED: return UV_ECONNREFUSED; - case EADDRINUSE: return UV_EADDRINUSE; - case EADDRNOTAVAIL: return UV_EADDRNOTAVAIL; - default: return UV_UNKNOWN; - } -} - - -static uv_err_t uv_err_new_artificial(uv_handle_t* handle, int code) { - uv_err_t err; - err.sys_errno_ = 0; - err.code = code; - last_err = err; - return err; -} - - -static uv_err_t uv_err_new(uv_handle_t* handle, int sys_error) { - uv_err_t err; - err.sys_errno_ = sys_error; - err.code = uv_translate_sys_error(sys_error); - last_err = err; - return err; -} - - -int uv_close(uv_handle_t* handle, uv_close_cb close_cb) { - uv_tcp_t* tcp; - uv_async_t* async; - uv_timer_t* timer; - - handle->close_cb = close_cb; - - switch (handle->type) { - case UV_TCP: - tcp = (uv_tcp_t*) handle; - ev_io_stop(EV_DEFAULT_ &tcp->write_watcher); - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - break; - - case UV_PREPARE: - uv_prepare_stop((uv_prepare_t*) handle); - break; - - case UV_CHECK: - uv_check_stop((uv_check_t*) handle); - break; - - case UV_IDLE: - uv_idle_stop((uv_idle_t*) handle); - break; - - case UV_ASYNC: - async = (uv_async_t*)handle; - ev_async_stop(EV_DEFAULT_ &async->async_watcher); - ev_ref(EV_DEFAULT_UC); - break; - - case UV_TIMER: - timer = (uv_timer_t*)handle; - if (ev_is_active(&timer->timer_watcher)) { - ev_ref(EV_DEFAULT_UC); - } - ev_timer_stop(EV_DEFAULT_ &timer->timer_watcher); - break; - - default: - assert(0); - return -1; - } - - uv_flag_set(handle, UV_CLOSING); - - /* This is used to call the on_close callback in the next loop. */ - ev_idle_start(EV_DEFAULT_ &handle->next_watcher); - ev_feed_event(EV_DEFAULT_ &handle->next_watcher, EV_IDLE); - assert(ev_is_pending(&handle->next_watcher)); - - return 0; -} - - -void uv_init() { - /* Initialize the default ev loop. */ -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - ev_default_loop(EVBACKEND_KQUEUE); -#else - ev_default_loop(EVFLAG_AUTO); -#endif -} - - -int uv_run() { - ev_run(EV_DEFAULT_ 0); - return 0; -} - - -static void uv__handle_init(uv_handle_t* handle, uv_handle_type type) { - uv_counters()->handle_init++; - - handle->type = type; - handle->flags = 0; - - ev_init(&handle->next_watcher, uv__next); - handle->next_watcher.data = handle; - - /* Ref the loop until this handle is closed. See uv__finish_close. */ - ev_ref(EV_DEFAULT_UC); -} - - -int uv_tcp_init(uv_tcp_t* tcp) { - uv__handle_init((uv_handle_t*)tcp, UV_TCP); - uv_counters()->tcp_init++; - - tcp->alloc_cb = NULL; - tcp->connect_req = NULL; - tcp->accepted_fd = -1; - tcp->fd = -1; - tcp->delayed_error = 0; - ngx_queue_init(&tcp->write_queue); - ngx_queue_init(&tcp->write_completed_queue); - tcp->write_queue_size = 0; - - ev_init(&tcp->read_watcher, uv__tcp_io); - tcp->read_watcher.data = tcp; - - ev_init(&tcp->write_watcher, uv__tcp_io); - tcp->write_watcher.data = tcp; - - assert(ngx_queue_empty(&tcp->write_queue)); - assert(ngx_queue_empty(&tcp->write_completed_queue)); - assert(tcp->write_queue_size == 0); - - return 0; -} - - -int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, int addrsize) { - int r; - - if (tcp->fd <= 0) { - int fd = socket(domain, SOCK_STREAM, 0); - - if (fd < 0) { - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - - if (uv_tcp_open(tcp, fd)) { - close(fd); - return -2; - } - } - - assert(tcp->fd >= 0); - - r = bind(tcp->fd, addr, addrsize); - tcp->delayed_error = 0; - - if (r) { - switch (errno) { - case EADDRINUSE: - tcp->delayed_error = errno; - return 0; - - default: - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - } - - return 0; -} - - -int uv_tcp_bind(uv_tcp_t* tcp, struct sockaddr_in addr) { - if (addr.sin_family != AF_INET) { - uv_err_new((uv_handle_t*)tcp, EFAULT); - return -1; - } - - return uv__bind(tcp, AF_INET, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); -} - - -int uv_tcp_bind6(uv_tcp_t* tcp, struct sockaddr_in6 addr) { - if (addr.sin6_family != AF_INET6) { - uv_err_new((uv_handle_t*)tcp, EFAULT); - return -1; - } - - return uv__bind(tcp, AF_INET6, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)); -} - - -int uv_tcp_open(uv_tcp_t* tcp, int fd) { - int yes; - int r; - - assert(fd >= 0); - tcp->fd = fd; - - /* Set non-blocking. */ - yes = 1; - r = fcntl(fd, F_SETFL, O_NONBLOCK); - assert(r == 0); - - /* Reuse the port address. */ - r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - assert(r == 0); - - /* Associate the fd with each ev_io watcher. */ - ev_io_set(&tcp->read_watcher, fd, EV_READ); - ev_io_set(&tcp->write_watcher, fd, EV_WRITE); - - /* These should have been set up by uv_tcp_init. */ - assert(tcp->next_watcher.data == tcp); - assert(tcp->write_watcher.data == tcp); - assert(tcp->read_watcher.data == tcp); - assert(tcp->read_watcher.cb == uv__tcp_io); - assert(tcp->write_watcher.cb == uv__tcp_io); - - return 0; -} - - -void uv__server_io(EV_P_ ev_io* watcher, int revents) { - int fd; - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(struct sockaddr_storage); - uv_tcp_t* tcp = watcher->data; - - assert(watcher == &tcp->read_watcher || - watcher == &tcp->write_watcher); - assert(revents == EV_READ); - - assert(!uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING)); - - if (tcp->accepted_fd >= 0) { - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - return; - } - - while (1) { - assert(tcp->accepted_fd < 0); - fd = accept(tcp->fd, (struct sockaddr*)&addr, &addrlen); - - if (fd < 0) { - if (errno == EAGAIN) { - /* No problem. */ - return; - } else if (errno == EMFILE) { - /* TODO special trick. unlock reserved socket, accept, close. */ - return; - } else { - uv_err_new((uv_handle_t*)tcp, errno); - tcp->connection_cb((uv_handle_t*)tcp, -1); - } - - } else { - tcp->accepted_fd = fd; - tcp->connection_cb((uv_handle_t*)tcp, 0); - if (tcp->accepted_fd >= 0) { - /* The user hasn't yet accepted called uv_accept() */ - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - return; - } - } - } -} - - -int uv_accept(uv_handle_t* server, uv_stream_t* client) { - uv_tcp_t* tcpServer = (uv_tcp_t*)server; - uv_tcp_t* tcpClient = (uv_tcp_t*)client; - - if (tcpServer->accepted_fd < 0) { - uv_err_new(server, EAGAIN); - return -1; - } - - if (uv_tcp_open(tcpClient, tcpServer->accepted_fd)) { - /* Ignore error for now */ - tcpServer->accepted_fd = -1; - close(tcpServer->accepted_fd); - return -1; - } else { - tcpServer->accepted_fd = -1; - ev_io_start(EV_DEFAULT_ &tcpServer->read_watcher); - return 0; - } -} - - -int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - int r; - - assert(tcp->fd >= 0); - - if (tcp->delayed_error) { - uv_err_new((uv_handle_t*)tcp, tcp->delayed_error); - return -1; - } - - r = listen(tcp->fd, backlog); - if (r < 0) { - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - - tcp->connection_cb = cb; - - /* Start listening for connections. */ - ev_io_set(&tcp->read_watcher, tcp->fd, EV_READ); - ev_set_cb(&tcp->read_watcher, uv__server_io); - ev_io_start(EV_DEFAULT_ &tcp->read_watcher); - - return 0; -} - - -void uv__finish_close(uv_handle_t* handle) { - uv_tcp_t* tcp; - - assert(uv_flag_is_set(handle, UV_CLOSING)); - assert(!uv_flag_is_set(handle, UV_CLOSED)); - uv_flag_set(handle, UV_CLOSED); - - switch (handle->type) { - case UV_TCP: - /* XXX Is it necessary to stop these watchers here? weren't they - * supposed to be stopped in uv_close()? - */ - tcp = (uv_tcp_t*)handle; - ev_io_stop(EV_DEFAULT_ &tcp->write_watcher); - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - - assert(!ev_is_active(&tcp->read_watcher)); - assert(!ev_is_active(&tcp->write_watcher)); - - close(tcp->fd); - tcp->fd = -1; - - if (tcp->accepted_fd >= 0) { - close(tcp->accepted_fd); - tcp->accepted_fd = -1; - } - break; - - case UV_PREPARE: - assert(!ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher)); - break; - - case UV_CHECK: - assert(!ev_is_active(&((uv_check_t*)handle)->check_watcher)); - break; - - case UV_IDLE: - assert(!ev_is_active(&((uv_idle_t*)handle)->idle_watcher)); - break; - - case UV_ASYNC: - assert(!ev_is_active(&((uv_async_t*)handle)->async_watcher)); - break; - - case UV_TIMER: - assert(!ev_is_active(&((uv_timer_t*)handle)->timer_watcher)); - break; - - default: - assert(0); - break; - } - - ev_idle_stop(EV_DEFAULT_ &handle->next_watcher); - - if (handle->close_cb) { - handle->close_cb(handle); - } - - ev_unref(EV_DEFAULT_UC); -} - - -uv_req_t* uv_write_queue_head(uv_tcp_t* tcp) { - ngx_queue_t* q; - uv_req_t* req; - - if (ngx_queue_empty(&tcp->write_queue)) { - return NULL; - } - - q = ngx_queue_head(&tcp->write_queue); - if (!q) { - return NULL; - } - - req = ngx_queue_data(q, struct uv_req_s, queue); - assert(req); - - return req; -} - - -void uv__next(EV_P_ ev_idle* watcher, int revents) { - uv_handle_t* handle = watcher->data; - assert(watcher == &handle->next_watcher); - assert(revents == EV_IDLE); - - /* For now this function is only to handle the closing event, but we might - * put more stuff here later. - */ - assert(uv_flag_is_set(handle, UV_CLOSING)); - uv__finish_close(handle); -} - - -static void uv__drain(uv_tcp_t* tcp) { - uv_req_t* req; - uv_shutdown_cb cb; - - assert(!uv_write_queue_head(tcp)); - assert(tcp->write_queue_size == 0); - - ev_io_stop(EV_DEFAULT_ &tcp->write_watcher); - - /* Shutdown? */ - if (uv_flag_is_set((uv_handle_t*)tcp, UV_SHUTTING) && - !uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING) && - !uv_flag_is_set((uv_handle_t*)tcp, UV_SHUT)) { - assert(tcp->shutdown_req); - - req = tcp->shutdown_req; - cb = (uv_shutdown_cb)req->cb; - - if (shutdown(tcp->fd, SHUT_WR)) { - /* Error. Report it. User should call uv_close(). */ - uv_err_new((uv_handle_t*)tcp, errno); - if (cb) cb(req, -1); - } else { - uv_err_new((uv_handle_t*)tcp, 0); - uv_flag_set((uv_handle_t*)tcp, UV_SHUT); - if (cb) cb(req, 0); - } - } -} - - -/* On success returns NULL. On error returns a pointer to the write request - * which had the error. - */ -static uv_req_t* uv__write(uv_tcp_t* tcp) { - uv_req_t* req; - struct iovec* iov; - int iovcnt; - ssize_t n; - - assert(tcp->fd >= 0); - - /* TODO: should probably while(1) here until EAGAIN */ - - /* Get the request at the head of the queue. */ - req = uv_write_queue_head(tcp); - if (!req) { - assert(tcp->write_queue_size == 0); - return NULL; - } - - assert(req->handle == (uv_handle_t*)tcp); - - /* Cast to iovec. We had to have our own uv_buf_t instead of iovec - * because Windows's WSABUF is not an iovec. - */ - assert(sizeof(uv_buf_t) == sizeof(struct iovec)); - iov = (struct iovec*) &(req->bufs[req->write_index]); - iovcnt = req->bufcnt - req->write_index; - - /* Now do the actual writev. Note that we've been updating the pointers - * inside the iov each time we write. So there is no need to offset it. - */ - - if (iovcnt == 1) { - n = write(tcp->fd, iov[0].iov_base, iov[0].iov_len); - } - else { - n = writev(tcp->fd, iov, iovcnt); - } - - if (n < 0) { - if (errno != EAGAIN) { - /* Error */ - uv_err_new((uv_handle_t*)tcp, errno); - return req; - } - } else { - /* Successful write */ - - /* Update the counters. */ - while (n > 0) { - uv_buf_t* buf = &(req->bufs[req->write_index]); - size_t len = buf->len; - - assert(req->write_index < req->bufcnt); - - if (n < len) { - buf->base += n; - buf->len -= n; - tcp->write_queue_size -= n; - n = 0; - - /* There is more to write. Break and ensure the watcher is pending. */ - break; - - } else { - /* Finished writing the buf at index req->write_index. */ - req->write_index++; - - assert(n >= len); - n -= len; - - assert(tcp->write_queue_size >= len); - tcp->write_queue_size -= len; - - if (req->write_index == req->bufcnt) { - /* Then we're done! */ - assert(n == 0); - - /* Pop the req off tcp->write_queue. */ - ngx_queue_remove(&req->queue); - if (req->bufs != req->bufsml) { - free(req->bufs); - } - req->bufs = NULL; - - /* Add it to the write_completed_queue where it will have its - * callback called in the near future. - * TODO: start trying to write the next request. - */ - ngx_queue_insert_tail(&tcp->write_completed_queue, &req->queue); - ev_feed_event(EV_DEFAULT_ &tcp->write_watcher, EV_WRITE); - return NULL; - } - } - } - } - - /* Either we've counted n down to zero or we've got EAGAIN. */ - assert(n == 0 || n == -1); - - /* We're not done. */ - ev_io_start(EV_DEFAULT_ &tcp->write_watcher); - - return NULL; -} - - -static void uv__write_callbacks(uv_tcp_t* tcp) { - uv_write_cb cb; - int callbacks_made = 0; - ngx_queue_t* q; - uv_req_t* req; - - while (!ngx_queue_empty(&tcp->write_completed_queue)) { - /* Pop a req off write_completed_queue. */ - q = ngx_queue_head(&tcp->write_completed_queue); - assert(q); - req = ngx_queue_data(q, struct uv_req_s, queue); - ngx_queue_remove(q); - - cb = (uv_write_cb) req->cb; - - /* NOTE: call callback AFTER freeing the request data. */ - if (cb) { - cb(req, 0); - } - - callbacks_made++; - } - - assert(ngx_queue_empty(&tcp->write_completed_queue)); - - /* Write queue drained. */ - if (!uv_write_queue_head(tcp)) { - uv__drain(tcp); - } -} - - -void uv__read(uv_tcp_t* tcp) { - uv_buf_t buf; - struct iovec* iov; - ssize_t nread; - - /* XXX: Maybe instead of having UV_READING we just test if - * tcp->read_cb is NULL or not? - */ - while (tcp->read_cb && uv_flag_is_set((uv_handle_t*)tcp, UV_READING)) { - assert(tcp->alloc_cb); - buf = tcp->alloc_cb((uv_stream_t*)tcp, 64 * 1024); - - assert(buf.len > 0); - assert(buf.base); - - iov = (struct iovec*) &buf; - - nread = read(tcp->fd, buf.base, buf.len); - - if (nread < 0) { - /* Error */ - if (errno == EAGAIN) { - /* Wait for the next one. */ - if (uv_flag_is_set((uv_handle_t*)tcp, UV_READING)) { - ev_io_start(EV_DEFAULT_UC_ &tcp->read_watcher); - } - uv_err_new((uv_handle_t*)tcp, EAGAIN); - tcp->read_cb((uv_stream_t*)tcp, 0, buf); - return; - } else { - /* Error. User should call uv_close(). */ - uv_err_new((uv_handle_t*)tcp, errno); - tcp->read_cb((uv_stream_t*)tcp, -1, buf); - assert(!ev_is_active(&tcp->read_watcher)); - return; - } - } else if (nread == 0) { - /* EOF */ - uv_err_new_artificial((uv_handle_t*)tcp, UV_EOF); - ev_io_stop(EV_DEFAULT_UC_ &tcp->read_watcher); - tcp->read_cb((uv_stream_t*)tcp, -1, buf); - return; - } else { - /* Successful read */ - tcp->read_cb((uv_stream_t*)tcp, nread, buf); - } - } -} - - -int uv_shutdown(uv_req_t* req) { - uv_tcp_t* tcp = (uv_tcp_t*)req->handle; - assert(tcp->fd >= 0); - assert(tcp->type == UV_TCP); - - if (uv_flag_is_set((uv_handle_t*)tcp, UV_SHUT) || - uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSED) || - uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING)) { - return -1; - } - - tcp->shutdown_req = req; - req->type = UV_SHUTDOWN; - - uv_flag_set((uv_handle_t*)tcp, UV_SHUTTING); - - ev_io_start(EV_DEFAULT_UC_ &tcp->write_watcher); - - return 0; -} - - -void uv__tcp_io(EV_P_ ev_io* watcher, int revents) { - uv_tcp_t* tcp = watcher->data; - assert(watcher == &tcp->read_watcher || - watcher == &tcp->write_watcher); - - assert(tcp->fd >= 0); - - assert(!uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING)); - - if (tcp->connect_req) { - uv__tcp_connect(tcp); - } else { - if (revents & EV_READ) { - uv__read(tcp); - } - - if (revents & EV_WRITE) { - uv_req_t* req = uv__write(tcp); - if (req) { - /* Error. Notify the user. */ - uv_write_cb cb = (uv_write_cb) req->cb; - - if (cb) { - cb(req, -1); - } - } else { - uv__write_callbacks(tcp); - } - } - } -} - - -/** - * We get called here from directly following a call to connect(2). - * In order to determine if we've errored out or succeeded must call - * getsockopt. - */ -static void uv__tcp_connect(uv_tcp_t* tcp) { - int error; - uv_req_t* req; - uv_connect_cb connect_cb; - socklen_t errorsize = sizeof(int); - - assert(tcp->fd >= 0); - - req = tcp->connect_req; - assert(req); - - if (tcp->delayed_error) { - /* To smooth over the differences between unixes errors that - * were reported synchronously on the first connect can be delayed - * until the next tick--which is now. - */ - error = tcp->delayed_error; - tcp->delayed_error = 0; - } else { - /* Normal situation: we need to get the socket error from the kernel. */ - getsockopt(tcp->fd, SOL_SOCKET, SO_ERROR, &error, &errorsize); - } - - if (!error) { - ev_io_start(EV_DEFAULT_ &tcp->read_watcher); - - /* Successful connection */ - tcp->connect_req = NULL; - connect_cb = (uv_connect_cb) req->cb; - if (connect_cb) { - connect_cb(req, 0); - } - - } else if (error == EINPROGRESS) { - /* Still connecting. */ - return; - } else { - /* Error */ - uv_err_t err = uv_err_new((uv_handle_t*)tcp, error); - - tcp->connect_req = NULL; - - connect_cb = (uv_connect_cb) req->cb; - if (connect_cb) { - connect_cb(req, -1); - } - } -} - - -static int uv__connect(uv_req_t* req, struct sockaddr* addr, - socklen_t addrlen) { - uv_tcp_t* tcp = (uv_tcp_t*)req->handle; - int r; - - if (tcp->fd <= 0) { - int fd = socket(addr->sa_family, SOCK_STREAM, 0); - - if (fd < 0) { - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - - if (uv_tcp_open(tcp, fd)) { - close(fd); - return -2; - } - } - - req->type = UV_CONNECT; - ngx_queue_init(&req->queue); - - if (tcp->connect_req) { - uv_err_new((uv_handle_t*)tcp, EALREADY); - return -1; - } - - if (tcp->type != UV_TCP) { - uv_err_new((uv_handle_t*)tcp, ENOTSOCK); - return -1; - } - - tcp->connect_req = req; - - r = connect(tcp->fd, addr, addrlen); - - tcp->delayed_error = 0; - - if (r != 0 && errno != EINPROGRESS) { - switch (errno) { - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. - */ - case ECONNREFUSED: - tcp->delayed_error = errno; - break; - - default: - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - } - - assert(tcp->write_watcher.data == tcp); - ev_io_start(EV_DEFAULT_ &tcp->write_watcher); - - if (tcp->delayed_error) { - ev_feed_event(EV_DEFAULT_ &tcp->write_watcher, EV_WRITE); - } - - return 0; -} - - -int uv_tcp_connect(uv_req_t* req, struct sockaddr_in addr) { - assert(addr.sin_family == AF_INET); - return uv__connect(req, (struct sockaddr*) &addr, - sizeof(struct sockaddr_in)); -} - - -int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6 addr) { - assert(addr.sin6_family == AF_INET6); - return uv__connect(req, (struct sockaddr*) &addr, - sizeof(struct sockaddr_in6)); -} - - -static size_t uv__buf_count(uv_buf_t bufs[], int bufcnt) { - size_t total = 0; - int i; - - for (i = 0; i < bufcnt; i++) { - total += bufs[i].len; - } - - return total; -} - - -/* The buffers to be written must remain valid until the callback is called. - * This is not required for the uv_buf_t array. - */ -int uv_write(uv_req_t* req, uv_buf_t bufs[], int bufcnt) { - uv_tcp_t* tcp = (uv_tcp_t*)req->handle; - int empty_queue = (tcp->write_queue_size == 0); - assert(tcp->fd >= 0); - - ngx_queue_init(&req->queue); - req->type = UV_WRITE; - - - if (bufcnt < UV_REQ_BUFSML_SIZE) { - req->bufs = req->bufsml; - } - else { - req->bufs = malloc(sizeof(uv_buf_t) * bufcnt); - } - - memcpy(req->bufs, bufs, bufcnt * sizeof(uv_buf_t)); - req->bufcnt = bufcnt; - - /* - * fprintf(stderr, "cnt: %d bufs: %p bufsml: %p\n", bufcnt, req->bufs, req->bufsml); - */ - - req->write_index = 0; - tcp->write_queue_size += uv__buf_count(bufs, bufcnt); - - /* Append the request to write_queue. */ - ngx_queue_insert_tail(&tcp->write_queue, &req->queue); - - assert(!ngx_queue_empty(&tcp->write_queue)); - assert(tcp->write_watcher.cb == uv__tcp_io); - assert(tcp->write_watcher.data == tcp); - assert(tcp->write_watcher.fd == tcp->fd); - - /* If the queue was empty when this function began, we should attempt to - * do the write immediately. Otherwise start the write_watcher and wait - * for the fd to become writable. - */ - if (empty_queue) { - if (uv__write(tcp)) { - /* Error. uv_last_error has been set. */ - return -1; - } - } - - /* If the queue is now empty - we've flushed the request already. That - * means we need to make the callback. The callback can only be done on a - * fresh stack so we feed the event loop in order to service it. - */ - if (ngx_queue_empty(&tcp->write_queue)) { - ev_feed_event(EV_DEFAULT_ &tcp->write_watcher, EV_WRITE); - } else { - /* Otherwise there is data to write - so we should wait for the file - * descriptor to become writable. - */ - ev_io_start(EV_DEFAULT_ &tcp->write_watcher); - } - - return 0; -} - - -void uv_ref() { - ev_ref(EV_DEFAULT_UC); -} - - -void uv_unref() { - ev_unref(EV_DEFAULT_UC); -} - - -void uv_update_time() { - ev_now_update(EV_DEFAULT_UC); -} - - -int64_t uv_now() { - return (int64_t)(ev_now(EV_DEFAULT_UC) * 1000); -} - - -int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { - uv_tcp_t* tcp = (uv_tcp_t*)stream; - - /* The UV_READING flag is irrelevant of the state of the tcp - it just - * expresses the desired state of the user. - */ - uv_flag_set((uv_handle_t*)tcp, UV_READING); - - /* TODO: try to do the read inline? */ - /* TODO: keep track of tcp state. If we've gotten a EOF then we should - * not start the IO watcher. - */ - assert(tcp->fd >= 0); - assert(alloc_cb); - - tcp->read_cb = read_cb; - tcp->alloc_cb = alloc_cb; - - /* These should have been set by uv_tcp_init. */ - assert(tcp->read_watcher.data == tcp); - assert(tcp->read_watcher.cb == uv__tcp_io); - - ev_io_start(EV_DEFAULT_UC_ &tcp->read_watcher); - return 0; -} - - -int uv_read_stop(uv_stream_t* stream) { - uv_tcp_t* tcp = (uv_tcp_t*)stream; - - uv_flag_unset((uv_handle_t*)tcp, UV_READING); - - ev_io_stop(EV_DEFAULT_UC_ &tcp->read_watcher); - tcp->read_cb = NULL; - tcp->alloc_cb = NULL; - return 0; -} - - -void uv_req_init(uv_req_t* req, uv_handle_t* handle, void *(*cb)(void *)) { - uv_counters()->req_init++; - req->type = UV_UNKNOWN_REQ; - req->cb = cb; - req->handle = handle; - ngx_queue_init(&req->queue); -} - - -static void uv__prepare(EV_P_ ev_prepare* w, int revents) { - uv_prepare_t* prepare = w->data; - - if (prepare->prepare_cb) { - prepare->prepare_cb(prepare, 0); - } -} - - -int uv_prepare_init(uv_prepare_t* prepare) { - uv__handle_init((uv_handle_t*)prepare, UV_PREPARE); - uv_counters()->prepare_init++; - - ev_prepare_init(&prepare->prepare_watcher, uv__prepare); - prepare->prepare_watcher.data = prepare; - - prepare->prepare_cb = NULL; - - return 0; -} - - -int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) { - int was_active = ev_is_active(&prepare->prepare_watcher); - - prepare->prepare_cb = cb; - - ev_prepare_start(EV_DEFAULT_UC_ &prepare->prepare_watcher); - - if (!was_active) { - ev_unref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_prepare_stop(uv_prepare_t* prepare) { - int was_active = ev_is_active(&prepare->prepare_watcher); - - ev_prepare_stop(EV_DEFAULT_UC_ &prepare->prepare_watcher); - - if (was_active) { - ev_ref(EV_DEFAULT_UC); - } - return 0; -} - - - -static void uv__check(EV_P_ ev_check* w, int revents) { - uv_check_t* check = w->data; - - if (check->check_cb) { - check->check_cb(check, 0); - } -} - - -int uv_check_init(uv_check_t* check) { - uv__handle_init((uv_handle_t*)check, UV_CHECK); - uv_counters()->check_init; - - ev_check_init(&check->check_watcher, uv__check); - check->check_watcher.data = check; - - check->check_cb = NULL; - - return 0; -} - - -int uv_check_start(uv_check_t* check, uv_check_cb cb) { - int was_active = ev_is_active(&check->check_watcher); - - check->check_cb = cb; - - ev_check_start(EV_DEFAULT_UC_ &check->check_watcher); - - if (!was_active) { - ev_unref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_check_stop(uv_check_t* check) { - int was_active = ev_is_active(&check->check_watcher); - - ev_check_stop(EV_DEFAULT_UC_ &check->check_watcher); - - if (was_active) { - ev_ref(EV_DEFAULT_UC); - } - - return 0; -} - - -static void uv__idle(EV_P_ ev_idle* w, int revents) { - uv_idle_t* idle = (uv_idle_t*)(w->data); - - if (idle->idle_cb) { - idle->idle_cb(idle, 0); - } -} - - - -int uv_idle_init(uv_idle_t* idle) { - uv__handle_init((uv_handle_t*)idle, UV_IDLE); - uv_counters()->idle_init++; - - ev_idle_init(&idle->idle_watcher, uv__idle); - idle->idle_watcher.data = idle; - - idle->idle_cb = NULL; - - return 0; -} - - -int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) { - int was_active = ev_is_active(&idle->idle_watcher); - - idle->idle_cb = cb; - ev_idle_start(EV_DEFAULT_UC_ &idle->idle_watcher); - - if (!was_active) { - ev_unref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_idle_stop(uv_idle_t* idle) { - int was_active = ev_is_active(&idle->idle_watcher); - - ev_idle_stop(EV_DEFAULT_UC_ &idle->idle_watcher); - - if (was_active) { - ev_ref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_is_active(uv_handle_t* handle) { - switch (handle->type) { - case UV_TIMER: - return ev_is_active(&((uv_timer_t*)handle)->timer_watcher); - - case UV_PREPARE: - return ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher); - - case UV_CHECK: - return ev_is_active(&((uv_check_t*)handle)->check_watcher); - - case UV_IDLE: - return ev_is_active(&((uv_idle_t*)handle)->idle_watcher); - - default: - return 1; - } -} - - -static void uv__async(EV_P_ ev_async* w, int revents) { - uv_async_t* async = w->data; - - if (async->async_cb) { - async->async_cb(async, 0); - } -} - - -int uv_async_init(uv_async_t* async, uv_async_cb async_cb) { - uv__handle_init((uv_handle_t*)async, UV_ASYNC); - uv_counters()->async_init++; - - ev_async_init(&async->async_watcher, uv__async); - async->async_watcher.data = async; - - async->async_cb = async_cb; - - /* Note: This does not have symmetry with the other libev wrappers. */ - ev_async_start(EV_DEFAULT_UC_ &async->async_watcher); - ev_unref(EV_DEFAULT_UC); - - return 0; -} - - -int uv_async_send(uv_async_t* async) { - ev_async_send(EV_DEFAULT_UC_ &async->async_watcher); -} - - -static void uv__timer_cb(EV_P_ ev_timer* w, int revents) { - uv_timer_t* timer = w->data; - - if (!ev_is_active(w)) { - ev_ref(EV_DEFAULT_UC); - } - - if (timer->timer_cb) { - timer->timer_cb(timer, 0); - } -} - - -int uv_timer_init(uv_timer_t* timer) { - uv__handle_init((uv_handle_t*)timer, UV_TIMER); - uv_counters()->timer_init++; - - ev_init(&timer->timer_watcher, uv__timer_cb); - timer->timer_watcher.data = timer; - - return 0; -} - - -int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, - int64_t repeat) { - if (ev_is_active(&timer->timer_watcher)) { - return -1; - } - - timer->timer_cb = cb; - ev_timer_set(&timer->timer_watcher, timeout / 1000.0, repeat / 1000.0); - ev_timer_start(EV_DEFAULT_UC_ &timer->timer_watcher); - ev_unref(EV_DEFAULT_UC); - return 0; -} - - -int uv_timer_stop(uv_timer_t* timer) { - if (ev_is_active(&timer->timer_watcher)) { - ev_ref(EV_DEFAULT_UC); - } - - ev_timer_stop(EV_DEFAULT_UC_ &timer->timer_watcher); - return 0; -} - - -int uv_timer_again(uv_timer_t* timer) { - if (!ev_is_active(&timer->timer_watcher)) { - uv_err_new((uv_handle_t*)timer, EINVAL); - return -1; - } - - ev_timer_again(EV_DEFAULT_UC_ &timer->timer_watcher); - return 0; -} - -void uv_timer_set_repeat(uv_timer_t* timer, int64_t repeat) { - assert(timer->type == UV_TIMER); - timer->timer_watcher.repeat = repeat / 1000.0; -} - -int64_t uv_timer_get_repeat(uv_timer_t* timer) { - assert(timer->type == UV_TIMER); - return (int64_t)(1000 * timer->timer_watcher.repeat); -} - - -/* - * This is called once per second by ares_data.timer. It is used to - * constantly callback into c-ares for possibly processing timeouts. - */ -static void uv__ares_timeout(EV_P_ struct ev_timer* watcher, int revents) { - assert(watcher == &ares_data.timer); - assert(revents == EV_TIMER); - assert(!uv_ares_handles_empty()); - ares_process_fd(ares_data.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); -} - - -static void uv__ares_io(EV_P_ struct ev_io* watcher, int revents) { - /* Reset the idle timer */ - ev_timer_again(EV_A_ &ares_data.timer); - - /* Process DNS responses */ - ares_process_fd(ares_data.channel, - revents & EV_READ ? watcher->fd : ARES_SOCKET_BAD, - revents & EV_WRITE ? watcher->fd : ARES_SOCKET_BAD); -} - - -/* Allocates and returns a new uv_ares_task_t */ -static uv_ares_task_t* uv__ares_task_create(int fd) { - uv_ares_task_t* h = malloc(sizeof(uv_ares_task_t)); - - if (h == NULL) { - uv_fatal_error(ENOMEM, "malloc"); - } - - h->sock = fd; - - ev_io_init(&h->read_watcher, uv__ares_io, fd, EV_READ); - ev_io_init(&h->write_watcher, uv__ares_io, fd, EV_WRITE); - - h->read_watcher.data = h; - h->write_watcher.data = h; -} - - -/* Callback from ares when socket operation is started */ -static void uv__ares_sockstate_cb(void* data, ares_socket_t sock, - int read, int write) { - uv_ares_task_t* h = uv_find_ares_handle(sock); - - if (read || write) { - if (!h) { - /* New socket */ - - /* If this is the first socket then start the timer. */ - if (!ev_is_active(&ares_data.timer)) { - assert(uv_ares_handles_empty()); - ev_timer_again(EV_DEFAULT_UC_ &ares_data.timer); - } - - h = uv__ares_task_create(sock); - uv_add_ares_handle(h); - } - - if (read) { - ev_io_start(EV_DEFAULT_UC_ &h->read_watcher); - } else { - ev_io_stop(EV_DEFAULT_UC_ &h->read_watcher); - } - - if (write) { - ev_io_start(EV_DEFAULT_UC_ &h->write_watcher); - } else { - ev_io_stop(EV_DEFAULT_UC_ &h->write_watcher); - } - - } else { - /* - * read == 0 and write == 0 this is c-ares's way of notifying us that - * the socket is now closed. We must free the data associated with - * socket. - */ - assert(h && "When an ares socket is closed we should have a handle for it"); - - ev_io_stop(EV_DEFAULT_UC_ &h->read_watcher); - ev_io_stop(EV_DEFAULT_UC_ &h->write_watcher); - - uv_remove_ares_handle(h); - free(h); - - if (uv_ares_handles_empty()) { - ev_timer_stop(EV_DEFAULT_UC_ &ares_data.timer); - } - } -} - - -/* c-ares integration initialize and terminate */ -/* TODO: share this with windows? */ -int uv_ares_init_options(ares_channel *channelptr, - struct ares_options *options, - int optmask) { - int rc; - - /* only allow single init at a time */ - if (ares_data.channel != NULL) { - uv_err_new_artificial(NULL, UV_EALREADY); - return -1; - } - - /* set our callback as an option */ - options->sock_state_cb = uv__ares_sockstate_cb; - options->sock_state_cb_data = &ares_data; - optmask |= ARES_OPT_SOCK_STATE_CB; - - /* We do the call to ares_init_option for caller. */ - rc = ares_init_options(channelptr, options, optmask); - - /* if success, save channel */ - if (rc == ARES_SUCCESS) { - ares_data.channel = *channelptr; - } - - /* - * Initialize the timeout timer. The timer won't be started until the - * first socket is opened. - */ - ev_init(&ares_data.timer, uv__ares_timeout); - ares_data.timer.repeat = 1.0; - - return rc; -} - - -/* TODO share this with windows? */ -void uv_ares_destroy(ares_channel channel) { - /* only allow destroy if did init */ - if (ares_data.channel != NULL) { - ev_timer_stop(EV_DEFAULT_UC_ &ares_data.timer); - ares_destroy(channel); - ares_data.channel = NULL; - } -} - - -static int uv_getaddrinfo_done(eio_req* req) { - uv_getaddrinfo_t* handle = req->data; - - uv_unref(); - - free(handle->hints); - free(handle->service); - free(handle->hostname); - - if (handle->retcode != 0) { - /* TODO how to display gai error strings? */ - uv_err_new(NULL, handle->retcode); - } - - handle->cb(handle, handle->retcode, handle->res); - - freeaddrinfo(handle->res); - handle->res = NULL; - - return 0; -} - - -static int getaddrinfo_thread_proc(eio_req *req) { - uv_getaddrinfo_t* handle = req->data; - - handle->retcode = getaddrinfo(handle->hostname, - handle->service, - handle->hints, - &handle->res); - return 0; -} - - -/* stub implementation of uv_getaddrinfo */ -int uv_getaddrinfo(uv_getaddrinfo_t* handle, - uv_getaddrinfo_cb cb, - const char* hostname, - const char* service, - const struct addrinfo* hints) { - eio_req* req; - uv_eio_init(); - - if (handle == NULL || cb == NULL || - (hostname == NULL && service == NULL)) { - uv_err_new_artificial(NULL, UV_EINVAL); - return -1; - } - - memset(handle, 0, sizeof(uv_getaddrinfo_t)); - - /* TODO don't alloc so much. */ - - if (hints) { - handle->hints = malloc(sizeof(struct addrinfo)); - memcpy(&handle->hints, hints, sizeof(struct addrinfo)); - } - - /* TODO security! check lengths, check return values. */ - - handle->cb = cb; - handle->hostname = hostname ? strdup(hostname) : NULL; - handle->service = service ? strdup(service) : NULL; - - /* TODO check handle->hostname == NULL */ - /* TODO check handle->service == NULL */ - - uv_ref(); - - req = eio_custom(getaddrinfo_thread_proc, EIO_PRI_DEFAULT, - uv_getaddrinfo_done, handle); - assert(req); - assert(req->data == handle); - - return 0; -} - diff --git a/src/rt/libuv/src/uv-win.c b/src/rt/libuv/src/uv-win.c deleted file mode 100644 index 93bf64d193c..00000000000 --- a/src/rt/libuv/src/uv-win.c +++ /dev/null @@ -1,2455 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "uv.h" -#include "uv-common.h" -#include "tree.h" - -/* - * Guids and typedefs for winsock extension functions - * Mingw32 doesn't have these :-( - */ -#ifndef WSAID_ACCEPTEX -# define WSAID_ACCEPTEX \ - {0xb5367df1, 0xcbac, 0x11cf, \ - {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} - -# define WSAID_CONNECTEX \ - {0x25a207b9, 0xddf3, 0x4660, \ - {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} - -# define WSAID_GETACCEPTEXSOCKADDRS \ - {0xb5367df2, 0xcbac, 0x11cf, \ - {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} - -# define WSAID_DISCONNECTEX \ - {0x7fda2e11, 0x8630, 0x436f, \ - {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} - -# define WSAID_TRANSMITFILE \ - {0xb5367df0, 0xcbac, 0x11cf, \ - {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} - - typedef BOOL PASCAL (*LPFN_ACCEPTEX) - (SOCKET sListenSocket, - SOCKET sAcceptSocket, - PVOID lpOutputBuffer, - DWORD dwReceiveDataLength, - DWORD dwLocalAddressLength, - DWORD dwRemoteAddressLength, - LPDWORD lpdwBytesReceived, - LPOVERLAPPED lpOverlapped); - - typedef BOOL PASCAL (*LPFN_CONNECTEX) - (SOCKET s, - const struct sockaddr* name, - int namelen, - PVOID lpSendBuffer, - DWORD dwSendDataLength, - LPDWORD lpdwBytesSent, - LPOVERLAPPED lpOverlapped); - - typedef void PASCAL (*LPFN_GETACCEPTEXSOCKADDRS) - (PVOID lpOutputBuffer, - DWORD dwReceiveDataLength, - DWORD dwLocalAddressLength, - DWORD dwRemoteAddressLength, - LPSOCKADDR* LocalSockaddr, - LPINT LocalSockaddrLength, - LPSOCKADDR* RemoteSockaddr, - LPINT RemoteSockaddrLength); - - typedef BOOL PASCAL (*LPFN_DISCONNECTEX) - (SOCKET hSocket, - LPOVERLAPPED lpOverlapped, - DWORD dwFlags, - DWORD reserved); - - typedef BOOL PASCAL (*LPFN_TRANSMITFILE) - (SOCKET hSocket, - HANDLE hFile, - DWORD nNumberOfBytesToWrite, - DWORD nNumberOfBytesPerSend, - LPOVERLAPPED lpOverlapped, - LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, - DWORD dwFlags); -#endif - -/* - * MinGW is missing this too - */ -#ifndef SO_UPDATE_CONNECT_CONTEXT -# define SO_UPDATE_CONNECT_CONTEXT 0x7010 -#endif - - -/* - * MinGW is missing this too - */ -#ifndef _MSC_VER - typedef struct addrinfoW { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - wchar_t* ai_canonname; - struct sockaddr* ai_addr; - struct addrinfoW* ai_next; - } ADDRINFOW, *PADDRINFOW; - - DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const wchar_t* node, - const wchar_t* service, - const ADDRINFOW* hints, - PADDRINFOW* result); - - DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo); -#endif - - -/* - * Pointers to winsock extension functions to be retrieved dynamically - */ -static LPFN_CONNECTEX pConnectEx; -static LPFN_ACCEPTEX pAcceptEx; -static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; -static LPFN_DISCONNECTEX pDisconnectEx; -static LPFN_TRANSMITFILE pTransmitFile; -/* need IPv6 versions of winsock extension functions */ -static LPFN_CONNECTEX pConnectEx6; -static LPFN_ACCEPTEX pAcceptEx6; -static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; -static LPFN_DISCONNECTEX pDisconnectEx6; -static LPFN_TRANSMITFILE pTransmitFile6; - - -/* - * Private uv_handle flags - */ -#define UV_HANDLE_CLOSING 0x0001 -#define UV_HANDLE_CLOSED 0x0002 -#define UV_HANDLE_BOUND 0x0004 -#define UV_HANDLE_LISTENING 0x0008 -#define UV_HANDLE_CONNECTION 0x0010 -#define UV_HANDLE_CONNECTED 0x0020 -#define UV_HANDLE_READING 0x0040 -#define UV_HANDLE_ACTIVE 0x0040 -#define UV_HANDLE_EOF 0x0080 -#define UV_HANDLE_SHUTTING 0x0100 -#define UV_HANDLE_SHUT 0x0200 -#define UV_HANDLE_ENDGAME_QUEUED 0x0400 -#define UV_HANDLE_BIND_ERROR 0x1000 -#define UV_HANDLE_IPV6 0x2000 - -/* - * Private uv_req flags. - */ -/* The request is currently queued. */ -#define UV_REQ_PENDING 0x01 - - -/* Binary tree used to keep the list of timers sorted. */ -static int uv_timer_compare(uv_timer_t* handle1, uv_timer_t* handle2); -RB_HEAD(uv_timer_tree_s, uv_timer_s); -RB_PROTOTYPE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare) - -/* The head of the timers tree */ -static struct uv_timer_tree_s uv_timers_ = RB_INITIALIZER(uv_timers_); - - -/* Head of a single-linked list of closed handles */ -static uv_handle_t* uv_endgame_handles_ = NULL; - - -/* Tail of a single-linked circular queue of pending reqs. If the queue is */ -/* empty, tail_ is NULL. If there is only one item, tail_->next_req == tail_ */ -static uv_req_t* uv_pending_reqs_tail_ = NULL; - - -/* The current time according to the event loop. in msecs. */ -static int64_t uv_now_ = 0; -static int64_t uv_ticks_per_msec_ = 0; - - -/* - * Global I/O completion port - */ -static HANDLE uv_iocp_; - - -/* Global error code */ -static const uv_err_t uv_ok_ = { UV_OK, ERROR_SUCCESS }; -static uv_err_t uv_last_error_ = { UV_OK, ERROR_SUCCESS }; - -/* Error message string */ -static char* uv_err_str_ = NULL; - - -/* Reference count that keeps the event loop alive */ -static int uv_refs_ = 0; - - -/* Ip address used to bind to any port at any interface */ -static struct sockaddr_in uv_addr_ip4_any_; -static struct sockaddr_in6 uv_addr_ip6_any_; - - -/* A zero-size buffer for use by uv_read */ -static char uv_zero_[] = ""; - - -/* mark if IPv6 sockets are supported */ -static BOOL uv_allow_ipv6 = FALSE; - -/* - * Subclass of uv_handle_t. Used for integration of c-ares. - */ -typedef struct uv_ares_action_s uv_ares_action_t; - -struct uv_ares_action_s { - UV_HANDLE_FIELDS - struct uv_req_s ares_req; - SOCKET sock; - int read; - int write; -}; - -void uv_ares_process(uv_ares_action_t* handle, uv_req_t* req); -void uv_ares_task_cleanup(uv_ares_task_t* handle, uv_req_t* req); -void uv_ares_poll(uv_timer_t* handle, int status); - -/* memory used per ares_channel */ -struct uv_ares_channel_s { - ares_channel channel; - int activesockets; - uv_timer_t pollingtimer; -}; - -typedef struct uv_ares_channel_s uv_ares_channel_t; - -/* static data to hold single ares_channel */ -static uv_ares_channel_t uv_ares_data = { NULL, 0 }; - -/* default timeout per socket request if ares does not specify value */ -/* use 20 sec */ -#define ARES_TIMEOUT_MS 20000 - - -/* getaddrinfo integration */ -static void uv_getaddrinfo_done(uv_getaddrinfo_t* handle, uv_req_t* req); -/* adjust size value to be multiple of 4. Use to keep pointer aligned */ -/* Do we need different versions of this for different architectures? */ -#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) - - -/* Atomic set operation on char */ -#ifdef _MSC_VER /* MSVC */ - -/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */ -/* efficient than InterlockedExchange, but InterlockedExchange8 does not */ -/* exist, and interlocked operations on larger targets might require the */ -/* target to be aligned. */ -#pragma intrinsic(_InterlockedOr8) - -static char __declspec(inline) uv_atomic_exchange_set(char volatile* target) { - return _InterlockedOr8(target, 1); -} - -#else /* GCC */ - -/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ -static inline char uv_atomic_exchange_set(char volatile* target) { - const char one = 1; - char old_value; - __asm__ __volatile__ ("lock xchgb %0, %1\n\t" - : "=r"(old_value), "=m"(*target) - : "0"(one), "m"(*target) - : "memory"); - return old_value; -} - -#endif - - -/* - * Display an error message and abort the event loop. - */ -static void uv_fatal_error(const int errorno, const char* syscall) { - char* buf = NULL; - const char* errmsg; - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL); - - if (buf) { - errmsg = buf; - } else { - errmsg = "Unknown error"; - } - - /* FormatMessage messages include a newline character already, */ - /* so don't add another. */ - if (syscall) { - fprintf(stderr, "%s: (%d) %s", syscall, errorno, errmsg); - } else { - fprintf(stderr, "(%d) %s", errorno, errmsg); - } - - if (buf) { - LocalFree(buf); - } - - *((char*)NULL) = 0xff; /* Force debug break */ - abort(); -} - - -uv_err_t uv_last_error() { - return uv_last_error_; -} - - -char* uv_strerror(uv_err_t err) { - if (uv_err_str_ != NULL) { - LocalFree((void*) uv_err_str_); - } - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err.sys_errno_, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&uv_err_str_, 0, NULL); - - if (uv_err_str_) { - return uv_err_str_; - } else { - return "Unknown error"; - } -} - - -static uv_err_code uv_translate_sys_error(int sys_errno) { - switch (sys_errno) { - case ERROR_SUCCESS: return UV_OK; - case ERROR_NOACCESS: return UV_EACCESS; - case WSAEACCES: return UV_EACCESS; - case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; - case WSAEADDRINUSE: return UV_EADDRINUSE; - case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; - case WSAEWOULDBLOCK: return UV_EAGAIN; - case WSAEALREADY: return UV_EALREADY; - case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED; - case WSAECONNREFUSED: return UV_ECONNREFUSED; - case WSAEFAULT: return UV_EFAULT; - case ERROR_INVALID_DATA: return UV_EINVAL; - case WSAEINVAL: return UV_EINVAL; - case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; - case WSAEMFILE: return UV_EMFILE; - case ERROR_OUTOFMEMORY: return UV_ENOMEM; - case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL; - case ERROR_INVALID_FLAGS: return UV_EBADF; - case ERROR_INVALID_PARAMETER: return UV_EINVAL; - case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; - default: return UV_UNKNOWN; - } -} - - -static uv_err_t uv_new_sys_error(int sys_errno) { - uv_err_t e; - e.code = uv_translate_sys_error(sys_errno); - e.sys_errno_ = sys_errno; - return e; -} - - -static void uv_set_sys_error(int sys_errno) { - uv_last_error_.code = uv_translate_sys_error(sys_errno); - uv_last_error_.sys_errno_ = sys_errno; -} - - -/* - * Retrieves the pointer to a winsock extension function. - */ -static BOOL uv_get_extension_function(SOCKET socket, GUID guid, - void **target) { - DWORD result, bytes; - - result = WSAIoctl(socket, - SIO_GET_EXTENSION_FUNCTION_POINTER, - &guid, - sizeof(guid), - (void*)target, - sizeof(*target), - &bytes, - NULL, - NULL); - - if (result == SOCKET_ERROR) { - *target = NULL; - return FALSE; - } else { - return TRUE; - } -} - - -void uv_init() { - const GUID wsaid_connectex = WSAID_CONNECTEX; - const GUID wsaid_acceptex = WSAID_ACCEPTEX; - const GUID wsaid_getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; - const GUID wsaid_disconnectex = WSAID_DISCONNECTEX; - const GUID wsaid_transmitfile = WSAID_TRANSMITFILE; - - WSADATA wsa_data; - int errorno; - LARGE_INTEGER timer_frequency; - SOCKET dummy; - SOCKET dummy6; - - /* Initialize winsock */ - errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (errorno != 0) { - uv_fatal_error(errorno, "WSAStartup"); - } - - /* Set implicit binding address used by connectEx */ - uv_addr_ip4_any_ = uv_ip4_addr("0.0.0.0", 0); - uv_addr_ip6_any_ = uv_ip6_addr("::1", 0); - - /* Retrieve the needed winsock extension function pointers. */ - dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - if (dummy == INVALID_SOCKET) { - uv_fatal_error(WSAGetLastError(), "socket"); - } - - if (!uv_get_extension_function(dummy, - wsaid_connectex, - (void**)&pConnectEx) || - !uv_get_extension_function(dummy, - wsaid_acceptex, - (void**)&pAcceptEx) || - !uv_get_extension_function(dummy, - wsaid_getacceptexsockaddrs, - (void**)&pGetAcceptExSockAddrs) || - !uv_get_extension_function(dummy, - wsaid_disconnectex, - (void**)&pDisconnectEx) || - !uv_get_extension_function(dummy, - wsaid_transmitfile, - (void**)&pTransmitFile)) { - uv_fatal_error(WSAGetLastError(), - "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); - } - - if (closesocket(dummy) == SOCKET_ERROR) { - uv_fatal_error(WSAGetLastError(), "closesocket"); - } - -/* optional IPv6 versions of winsock extension functions */ - dummy6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); - if (dummy6 != INVALID_SOCKET) { - uv_allow_ipv6 = TRUE; - - if (!uv_get_extension_function(dummy6, - wsaid_connectex, - (void**)&pConnectEx6) || - !uv_get_extension_function(dummy6, - wsaid_acceptex, - (void**)&pAcceptEx6) || - !uv_get_extension_function(dummy6, - wsaid_getacceptexsockaddrs, - (void**)&pGetAcceptExSockAddrs6) || - !uv_get_extension_function(dummy6, - wsaid_disconnectex, - (void**)&pDisconnectEx6) || - !uv_get_extension_function(dummy6, - wsaid_transmitfile, - (void**)&pTransmitFile6)) { - uv_allow_ipv6 = FALSE; - } - - if (closesocket(dummy6) == SOCKET_ERROR) { - uv_fatal_error(WSAGetLastError(), "closesocket"); - } - } - - /* Create an I/O completion port */ - uv_iocp_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); - if (uv_iocp_ == NULL) { - uv_fatal_error(GetLastError(), "CreateIoCompletionPort"); - } - - /* Initialize the event loop time */ - if (!QueryPerformanceFrequency(&timer_frequency)) - uv_fatal_error(GetLastError(), "QueryPerformanceFrequency"); - uv_ticks_per_msec_ = timer_frequency.QuadPart / 1000; - - uv_update_time(); -} - - -void uv_req_init(uv_req_t* req, uv_handle_t* handle, void *(*cb)(void *)) { - uv_counters()->req_init++; - req->type = UV_UNKNOWN_REQ; - req->flags = 0; - req->handle = handle; - req->cb = cb; -} - - -static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { - return CONTAINING_RECORD(overlapped, uv_req_t, overlapped); -} - - -static void uv_insert_pending_req(uv_req_t* req) { - req->next_req = NULL; - if (uv_pending_reqs_tail_) { - req->next_req = uv_pending_reqs_tail_->next_req; - uv_pending_reqs_tail_ = req; - } else { - req->next_req = req; - uv_pending_reqs_tail_ = req; - } -} - - -static uv_req_t* uv_remove_pending_req() { - uv_req_t* req; - - if (uv_pending_reqs_tail_) { - req = uv_pending_reqs_tail_->next_req; - - if (req == uv_pending_reqs_tail_) { - uv_pending_reqs_tail_ = NULL; - } else { - uv_pending_reqs_tail_->next_req = req->next_req; - } - - return req; - - } else { - /* queue empty */ - return NULL; - } -} - - -static int uv_tcp_set_socket(uv_tcp_t* handle, SOCKET socket) { - DWORD yes = 1; - - assert(handle->socket == INVALID_SOCKET); - - /* Set the socket to nonblocking mode */ - if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - /* Make the socket non-inheritable */ - if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { - uv_set_sys_error(GetLastError()); - return -1; - } - - /* Associate it with the I/O completion port. */ - /* Use uv_handle_t pointer as completion key. */ - if (CreateIoCompletionPort((HANDLE)socket, - uv_iocp_, - (ULONG_PTR)socket, - 0) == NULL) { - uv_set_sys_error(GetLastError()); - return -1; - } - - handle->socket = socket; - - return 0; -} - - -static void uv_tcp_init_connection(uv_tcp_t* handle) { - handle->flags |= UV_HANDLE_CONNECTION; - handle->write_reqs_pending = 0; - uv_req_init(&(handle->read_req), (uv_handle_t*)handle, NULL); -} - - -int uv_tcp_init(uv_tcp_t* handle) { - handle->socket = INVALID_SOCKET; - handle->write_queue_size = 0; - handle->type = UV_TCP; - handle->flags = 0; - handle->reqs_pending = 0; - handle->error = uv_ok_; - handle->accept_socket = INVALID_SOCKET; - - uv_counters()->handle_init++; - uv_counters()->tcp_init++; - - uv_refs_++; - - return 0; -} - - -static void uv_tcp_endgame(uv_tcp_t* handle) { - uv_err_t err; - int status; - - if (handle->flags & UV_HANDLE_SHUTTING && - !(handle->flags & UV_HANDLE_SHUT) && - handle->write_reqs_pending == 0) { - - if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { - status = 0; - handle->flags |= UV_HANDLE_SHUT; - } else { - status = -1; - err = uv_new_sys_error(WSAGetLastError()); - } - if (handle->shutdown_req->cb) { - handle->shutdown_req->flags &= ~UV_REQ_PENDING; - if (status == -1) { - uv_last_error_ = err; - } - ((uv_shutdown_cb)handle->shutdown_req->cb)(handle->shutdown_req, status); - } - handle->reqs_pending--; - } - - if (handle->flags & UV_HANDLE_CLOSING && - handle->reqs_pending == 0) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb((uv_handle_t*)handle); - } - - uv_refs_--; - } -} - - -static void uv_timer_endgame(uv_timer_t* handle) { - if (handle->flags & UV_HANDLE_CLOSING) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb((uv_handle_t*)handle); - } - - uv_refs_--; - } -} - - -static void uv_loop_watcher_endgame(uv_handle_t* handle) { - if (handle->flags & UV_HANDLE_CLOSING) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb(handle); - } - - uv_refs_--; - } -} - - -static void uv_async_endgame(uv_async_t* handle) { - if (handle->flags & UV_HANDLE_CLOSING && - !handle->async_sent) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb((uv_handle_t*)handle); - } - - uv_refs_--; - } -} - - -static void uv_process_endgames() { - uv_handle_t* handle; - - while (uv_endgame_handles_) { - handle = uv_endgame_handles_; - uv_endgame_handles_ = handle->endgame_next; - - handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED; - - switch (handle->type) { - case UV_TCP: - uv_tcp_endgame((uv_tcp_t*)handle); - break; - - case UV_TIMER: - uv_timer_endgame((uv_timer_t*)handle); - break; - - case UV_PREPARE: - case UV_CHECK: - case UV_IDLE: - uv_loop_watcher_endgame(handle); - break; - - case UV_ASYNC: - uv_async_endgame((uv_async_t*)handle); - break; - - default: - assert(0); - break; - } - } -} - - -static void uv_want_endgame(uv_handle_t* handle) { - if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) { - handle->flags |= UV_HANDLE_ENDGAME_QUEUED; - - handle->endgame_next = uv_endgame_handles_; - uv_endgame_handles_ = handle; - } -} - - -static int uv_close_error(uv_handle_t* handle, uv_err_t e) { - uv_tcp_t* tcp; - - if (handle->flags & UV_HANDLE_CLOSING) { - return 0; - } - - handle->error = e; - handle->flags |= UV_HANDLE_CLOSING; - - /* Handle-specific close actions */ - switch (handle->type) { - case UV_TCP: - tcp = (uv_tcp_t*)handle; - tcp->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); - closesocket(tcp->socket); - if (tcp->reqs_pending == 0) { - uv_want_endgame(handle); - } - return 0; - - case UV_TIMER: - uv_timer_stop((uv_timer_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_PREPARE: - uv_prepare_stop((uv_prepare_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_CHECK: - uv_check_stop((uv_check_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_IDLE: - uv_idle_stop((uv_idle_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_ASYNC: - if (!((uv_async_t*)handle)->async_sent) { - uv_want_endgame(handle); - } - return 0; - - default: - /* Not supported */ - assert(0); - return -1; - } -} - - -int uv_close(uv_handle_t* handle, uv_close_cb close_cb) { - handle->close_cb = close_cb; - return uv_close_error(handle, uv_ok_); -} - - -int uv__bind(uv_tcp_t* handle, int domain, struct sockaddr* addr, int addrsize) { - DWORD err; - int r; - SOCKET sock; - - if (handle->socket == INVALID_SOCKET) { - sock = socket(domain, SOCK_STREAM, 0); - if (sock == INVALID_SOCKET) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - if (uv_tcp_set_socket(handle, sock) == -1) { - closesocket(sock); - return -1; - } - } - - r = bind(handle->socket, addr, addrsize); - - if (r == SOCKET_ERROR) { - err = WSAGetLastError(); - if (err == WSAEADDRINUSE) { - /* Some errors are not to be reported until connect() or listen() */ - handle->error = uv_new_sys_error(err); - handle->flags |= UV_HANDLE_BIND_ERROR; - } else { - uv_set_sys_error(err); - return -1; - } - } - - handle->flags |= UV_HANDLE_BOUND; - - return 0; -} - - -int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { - if (addr.sin_family != AF_INET) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - - return uv__bind(handle, AF_INET, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); -} - - -int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { - if (addr.sin6_family != AF_INET6) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - if (uv_allow_ipv6) { - handle->flags |= UV_HANDLE_IPV6; - return uv__bind(handle, AF_INET6, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)); - } else { - uv_new_sys_error(UV_EAFNOSUPPORT); - return -1; - } -} - - -static void uv_queue_accept(uv_tcp_t* handle) { - uv_req_t* req; - BOOL success; - DWORD bytes; - SOCKET accept_socket; - short family; - LPFN_ACCEPTEX pAcceptExFamily; - - assert(handle->flags & UV_HANDLE_LISTENING); - assert(handle->accept_socket == INVALID_SOCKET); - - /* Prepare the uv_req structure. */ - req = &handle->accept_req; - assert(!(req->flags & UV_REQ_PENDING)); - req->type = UV_ACCEPT; - req->flags |= UV_REQ_PENDING; - - /* choose family and extension function */ - if ((handle->flags & UV_HANDLE_IPV6) != 0) { - family = AF_INET6; - pAcceptExFamily = pAcceptEx6; - } else { - family = AF_INET; - pAcceptExFamily = pAcceptEx; - } - - /* Open a socket for the accepted connection. */ - accept_socket = socket(family, SOCK_STREAM, 0); - if (accept_socket == INVALID_SOCKET) { - req->error = uv_new_sys_error(WSAGetLastError()); - uv_insert_pending_req(req); - return; - } - - /* Prepare the overlapped structure. */ - memset(&(req->overlapped), 0, sizeof(req->overlapped)); - - success = pAcceptExFamily(handle->socket, - accept_socket, - (void*)&handle->accept_buffer, - 0, - sizeof(struct sockaddr_storage), - sizeof(struct sockaddr_storage), - &bytes, - &req->overlapped); - - if (!success && WSAGetLastError() != ERROR_IO_PENDING) { - /* Make this req pending reporting an error. */ - req->error = uv_new_sys_error(WSAGetLastError()); - uv_insert_pending_req(req); - /* Destroy the preallocated client socket. */ - closesocket(accept_socket); - return; - } - - handle->accept_socket = accept_socket; - - handle->reqs_pending++; - req->flags |= UV_REQ_PENDING; -} - - -static void uv_queue_read(uv_tcp_t* handle) { - uv_req_t* req; - uv_buf_t buf; - int result; - DWORD bytes, flags; - - assert(handle->flags & UV_HANDLE_READING); - - req = &handle->read_req; - assert(!(req->flags & UV_REQ_PENDING)); - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_READ; - - buf.base = (char*) &uv_zero_; - buf.len = 0; - - flags = 0; - result = WSARecv(handle->socket, - (WSABUF*)&buf, - 1, - &bytes, - &flags, - &req->overlapped, - NULL); - if (result != 0 && WSAGetLastError() != ERROR_IO_PENDING) { - /* Make this req pending reporting an error. */ - req->error = uv_new_sys_error(WSAGetLastError()); - uv_insert_pending_req(req); - return; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; -} - - -int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { - assert(backlog > 0); - - if (handle->flags & UV_HANDLE_BIND_ERROR) { - uv_last_error_ = handle->error; - return -1; - } - - if (handle->flags & UV_HANDLE_LISTENING || - handle->flags & UV_HANDLE_READING) { - /* Already listening. */ - uv_set_sys_error(WSAEALREADY); - return -1; - } - - if (listen(handle->socket, backlog) == SOCKET_ERROR) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - handle->flags |= UV_HANDLE_LISTENING; - handle->connection_cb = cb; - - uv_req_init(&(handle->accept_req), (uv_handle_t*)handle, NULL); - uv_queue_accept(handle); - - return 0; -} - - -int uv_accept(uv_handle_t* server, uv_stream_t* client) { - int rv = 0; - uv_tcp_t* tcpServer = (uv_tcp_t*)server; - uv_tcp_t* tcpClient = (uv_tcp_t*)client; - - if (tcpServer->accept_socket == INVALID_SOCKET) { - uv_set_sys_error(WSAENOTCONN); - return -1; - } - - if (uv_tcp_set_socket(tcpClient, tcpServer->accept_socket) == -1) { - closesocket(tcpServer->accept_socket); - rv = -1; - } else { - uv_tcp_init_connection(tcpClient); - } - - tcpServer->accept_socket = INVALID_SOCKET; - - if (!(tcpServer->flags & UV_HANDLE_CLOSING)) { - uv_queue_accept(tcpServer); - } - - return rv; -} - - -int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - uv_set_sys_error(WSAEINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_READING) { - uv_set_sys_error(WSAEALREADY); - return -1; - } - - if (handle->flags & UV_HANDLE_EOF) { - uv_set_sys_error(WSAESHUTDOWN); - return -1; - } - - handle->flags |= UV_HANDLE_READING; - handle->read_cb = read_cb; - handle->alloc_cb = alloc_cb; - - /* If reading was stopped and then started again, there could stell be a */ - /* read request pending. */ - if (!(handle->read_req.flags & UV_REQ_PENDING)) - uv_queue_read((uv_tcp_t*)handle); - - return 0; -} - - -int uv_read_stop(uv_stream_t* handle) { - handle->flags &= ~UV_HANDLE_READING; - - return 0; -} - - -int uv_tcp_connect(uv_req_t* req, struct sockaddr_in addr) { - int addrsize = sizeof(struct sockaddr_in); - BOOL success; - DWORD bytes; - uv_tcp_t* handle = (uv_tcp_t*)req->handle; - - assert(!(req->flags & UV_REQ_PENDING)); - - if (handle->flags & UV_HANDLE_BIND_ERROR) { - uv_last_error_ = handle->error; - return -1; - } - - if (addr.sin_family != AF_INET) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - - if (!(handle->flags & UV_HANDLE_BOUND) && - uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) - return -1; - - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_CONNECT; - - success = pConnectEx(handle->socket, - (struct sockaddr*)&addr, - addrsize, - NULL, - 0, - &bytes, - &req->overlapped); - - if (!success && WSAGetLastError() != ERROR_IO_PENDING) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; - - return 0; -} - - -int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6 addr) { - int addrsize = sizeof(struct sockaddr_in6); - BOOL success; - DWORD bytes; - uv_tcp_t* handle = (uv_tcp_t*)req->handle; - - if (!uv_allow_ipv6) { - uv_new_sys_error(UV_EAFNOSUPPORT); - return -1; - } - - assert(!(req->flags & UV_REQ_PENDING)); - - if (handle->flags & UV_HANDLE_BIND_ERROR) { - uv_last_error_ = handle->error; - return -1; - } - - if (addr.sin6_family != AF_INET6) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - - if (!(handle->flags & UV_HANDLE_BOUND) && - uv_tcp_bind6(handle, uv_addr_ip6_any_) < 0) - return -1; - - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_CONNECT; - - success = pConnectEx6(handle->socket, - (struct sockaddr*)&addr, - addrsize, - NULL, - 0, - &bytes, - &req->overlapped); - - if (!success && WSAGetLastError() != ERROR_IO_PENDING) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; - - return 0; -} - - -static size_t uv_count_bufs(uv_buf_t bufs[], int count) { - size_t bytes = 0; - int i; - - for (i = 0; i < count; i++) { - bytes += (size_t)bufs[i].len; - } - - return bytes; -} - - -int uv_write(uv_req_t* req, uv_buf_t bufs[], int bufcnt) { - int result; - DWORD bytes, err; - uv_tcp_t* handle = (uv_tcp_t*) req->handle; - - assert(!(req->flags & UV_REQ_PENDING)); - - if (!(req->handle->flags & UV_HANDLE_CONNECTION)) { - uv_set_sys_error(WSAEINVAL); - return -1; - } - - if (req->handle->flags & UV_HANDLE_SHUTTING) { - uv_set_sys_error(WSAESHUTDOWN); - return -1; - } - - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_WRITE; - - result = WSASend(handle->socket, - (WSABUF*)bufs, - bufcnt, - &bytes, - 0, - &req->overlapped, - NULL); - if (result != 0) { - err = WSAGetLastError(); - if (err != WSA_IO_PENDING) { - /* Send failed due to an error. */ - uv_set_sys_error(WSAGetLastError()); - return -1; - } - } - - if (result == 0) { - /* Request completed immediately. */ - req->queued_bytes = 0; - } else { - /* Request queued by the kernel. */ - req->queued_bytes = uv_count_bufs(bufs, bufcnt); - handle->write_queue_size += req->queued_bytes; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; - handle->write_reqs_pending++; - - return 0; -} - - -int uv_shutdown(uv_req_t* req) { - uv_tcp_t* handle = (uv_tcp_t*) req->handle; - int status = 0; - - if (!(req->handle->flags & UV_HANDLE_CONNECTION)) { - uv_set_sys_error(WSAEINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_SHUTTING) { - uv_set_sys_error(WSAESHUTDOWN); - return -1; - } - - req->type = UV_SHUTDOWN; - req->flags |= UV_REQ_PENDING; - - handle->flags |= UV_HANDLE_SHUTTING; - handle->shutdown_req = req; - handle->reqs_pending++; - - uv_want_endgame((uv_handle_t*)handle); - - return 0; -} - - -static void uv_tcp_return_req(uv_tcp_t* handle, uv_req_t* req) { - DWORD bytes, flags, err; - uv_buf_t buf; - - assert(handle->type == UV_TCP); - - /* Mark the request non-pending */ - req->flags &= ~UV_REQ_PENDING; - - switch (req->type) { - case UV_WRITE: - handle->write_queue_size -= req->queued_bytes; - if (req->cb) { - uv_last_error_ = req->error; - ((uv_write_cb)req->cb)(req, uv_last_error_.code == UV_OK ? 0 : -1); - } - handle->write_reqs_pending--; - if (handle->write_reqs_pending == 0 && - handle->flags & UV_HANDLE_SHUTTING) { - uv_want_endgame((uv_handle_t*)handle); - } - break; - - case UV_READ: - if (req->error.code != UV_OK) { - /* An error occurred doing the 0-read. */ - if (!(handle->flags & UV_HANDLE_READING)) { - break; - } - - /* Stop reading and report error. */ - handle->flags &= ~UV_HANDLE_READING; - uv_last_error_ = req->error; - buf.base = 0; - buf.len = 0; - handle->read_cb((uv_stream_t*)handle, -1, buf); - break; - } - - /* Do nonblocking reads until the buffer is empty */ - while (handle->flags & UV_HANDLE_READING) { - buf = handle->alloc_cb((uv_stream_t*)handle, 65536); - assert(buf.len > 0); - flags = 0; - if (WSARecv(handle->socket, - (WSABUF*)&buf, - 1, - &bytes, - &flags, - NULL, - NULL) != SOCKET_ERROR) { - if (bytes > 0) { - /* Successful read */ - handle->read_cb((uv_stream_t*)handle, bytes, buf); - /* Read again only if bytes == buf.len */ - if (bytes < buf.len) { - break; - } - } else { - /* Connection closed */ - handle->flags &= ~UV_HANDLE_READING; - handle->flags |= UV_HANDLE_EOF; - uv_last_error_.code = UV_EOF; - uv_last_error_.sys_errno_ = ERROR_SUCCESS; - handle->read_cb((uv_stream_t*)handle, -1, buf); - break; - } - } else { - err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK) { - /* Read buffer was completely empty, report a 0-byte read. */ - uv_set_sys_error(WSAEWOULDBLOCK); - handle->read_cb((uv_stream_t*)handle, 0, buf); - } else { - /* Ouch! serious error. */ - uv_set_sys_error(err); - handle->read_cb((uv_stream_t*)handle, -1, buf); - } - break; - } - } - /* Post another 0-read if still reading and not closing. */ - if (handle->flags & UV_HANDLE_READING) { - uv_queue_read(handle); - } - break; - - case UV_ACCEPT: - /* If handle->accepted_socket is not a valid socket, then */ - /* uv_queue_accept must have failed. This is a serious error. We stop */ - /* accepting connections and report this error to the connection */ - /* callback. */ - if (handle->accept_socket == INVALID_SOCKET) { - if (!(handle->flags & UV_HANDLE_LISTENING)) { - break; - } - handle->flags &= ~UV_HANDLE_LISTENING; - if (handle->connection_cb) { - uv_last_error_ = req->error; - handle->connection_cb((uv_handle_t*)handle, -1); - } - break; - } - - if (req->error.code == UV_OK && - setsockopt(handle->accept_socket, - SOL_SOCKET, - SO_UPDATE_ACCEPT_CONTEXT, - (char*)&handle->socket, - sizeof(handle->socket)) == 0) { - /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */ - if (handle->connection_cb) { - handle->connection_cb((uv_handle_t*)handle, 0); - } - } else { - /* Error related to accepted socket is ignored because the server */ - /* socket may still be healthy. If the server socket is broken - /* uv_queue_accept will detect it. */ - closesocket(handle->accept_socket); - if (handle->flags & UV_HANDLE_LISTENING) { - uv_queue_accept(handle); - } - } - break; - - case UV_CONNECT: - if (req->cb) { - if (req->error.code == UV_OK) { - if (setsockopt(handle->socket, - SOL_SOCKET, - SO_UPDATE_CONNECT_CONTEXT, - NULL, - 0) == 0) { - uv_tcp_init_connection(handle); - ((uv_connect_cb)req->cb)(req, 0); - } else { - uv_set_sys_error(WSAGetLastError()); - ((uv_connect_cb)req->cb)(req, -1); - } - } else { - uv_last_error_ = req->error; - ((uv_connect_cb)req->cb)(req, -1); - } - } - break; - - default: - assert(0); - } - - /* The number of pending requests is now down by one */ - handle->reqs_pending--; - - /* Queue the handle's close callback if it is closing and there are no */ - /* more pending requests. */ - if (handle->flags & UV_HANDLE_CLOSING && - handle->reqs_pending == 0) { - uv_want_endgame((uv_handle_t*)handle); - } -} - - -static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) { - if (a->due < b->due) - return -1; - if (a->due > b->due) - return 1; - if ((intptr_t)a < (intptr_t)b) - return -1; - if ((intptr_t)a > (intptr_t)b) - return 1; - return 0; -} - - -RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare); - - -int uv_timer_init(uv_timer_t* handle) { - uv_counters()->handle_init++; - uv_counters()->timer_init++; - - handle->type = UV_TIMER; - handle->flags = 0; - handle->error = uv_ok_; - handle->timer_cb = NULL; - handle->repeat = 0; - - uv_refs_++; - - return 0; -} - - -int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, int64_t timeout, int64_t repeat) { - if (handle->flags & UV_HANDLE_ACTIVE) { - RB_REMOVE(uv_timer_tree_s, &uv_timers_, handle); - } - - handle->timer_cb = (void*) timer_cb; - handle->due = uv_now_ + timeout; - handle->repeat = repeat; - handle->flags |= UV_HANDLE_ACTIVE; - - if (RB_INSERT(uv_timer_tree_s, &uv_timers_, handle) != NULL) { - uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); - } - - return 0; -} - - -int uv_timer_stop(uv_timer_t* handle) { - if (!(handle->flags & UV_HANDLE_ACTIVE)) - return 0; - - RB_REMOVE(uv_timer_tree_s, &uv_timers_, handle); - - handle->flags &= ~UV_HANDLE_ACTIVE; - - return 0; -} - - -int uv_timer_again(uv_timer_t* handle) { - /* If timer_cb is NULL that means that the timer was never started. */ - if (!handle->timer_cb) { - uv_set_sys_error(ERROR_INVALID_DATA); - return -1; - } - - if (handle->flags & UV_HANDLE_ACTIVE) { - RB_REMOVE(uv_timer_tree_s, &uv_timers_, handle); - handle->flags &= ~UV_HANDLE_ACTIVE; - } - - if (handle->repeat) { - handle->due = uv_now_ + handle->repeat; - - if (RB_INSERT(uv_timer_tree_s, &uv_timers_, handle) != NULL) { - uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); - } - - handle->flags |= UV_HANDLE_ACTIVE; - } - - return 0; -} - - -void uv_timer_set_repeat(uv_timer_t* handle, int64_t repeat) { - assert(handle->type == UV_TIMER); - handle->repeat = repeat; -} - - -int64_t uv_timer_get_repeat(uv_timer_t* handle) { - assert(handle->type == UV_TIMER); - return handle->repeat; -} - - -void uv_update_time() { - LARGE_INTEGER counter; - - if (!QueryPerformanceCounter(&counter)) - uv_fatal_error(GetLastError(), "QueryPerformanceCounter"); - - uv_now_ = counter.QuadPart / uv_ticks_per_msec_; -} - - -int64_t uv_now() { - return uv_now_; -} - - -#define UV_LOOP_WATCHER_DEFINE(name, NAME) \ - /* Lists of active loop (prepare / check / idle) watchers */ \ - static uv_##name##_t* uv_##name##_handles_ = NULL; \ - \ - /* This pointer will refer to the prepare/check/idle handle whose */ \ - /* callback is scheduled to be called next. This is needed to allow */ \ - /* safe removal from one of the lists above while that list being */ \ - /* iterated over. */ \ - static uv_##name##_t* uv_next_##name##_handle_ = NULL; \ - \ - \ - int uv_##name##_init(uv_##name##_t* handle) { \ - handle->type = UV_##NAME; \ - handle->flags = 0; \ - handle->error = uv_ok_; \ - \ - uv_refs_++; \ - \ - uv_counters()->handle_init++; \ - uv_counters()->prepare_init++; \ - \ - return 0; \ - } \ - \ - \ - int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ - uv_##name##_t* old_head; \ - \ - assert(handle->type == UV_##NAME); \ - \ - if (handle->flags & UV_HANDLE_ACTIVE) \ - return 0; \ - \ - old_head = uv_##name##_handles_; \ - \ - handle->name##_next = old_head; \ - handle->name##_prev = NULL; \ - \ - if (old_head) { \ - old_head->name##_prev = handle; \ - } \ - \ - uv_##name##_handles_ = handle; \ - \ - handle->name##_cb = cb; \ - handle->flags |= UV_HANDLE_ACTIVE; \ - \ - return 0; \ - } \ - \ - \ - int uv_##name##_stop(uv_##name##_t* handle) { \ - assert(handle->type == UV_##NAME); \ - \ - if (!(handle->flags & UV_HANDLE_ACTIVE)) \ - return 0; \ - \ - /* Update loop head if needed */ \ - if (uv_##name##_handles_ == handle) { \ - uv_##name##_handles_ = handle->name##_next; \ - } \ - \ - /* Update the iterator-next pointer of needed */ \ - if (uv_next_##name##_handle_ == handle) { \ - uv_next_##name##_handle_ = handle->name##_next; \ - } \ - \ - if (handle->name##_prev) { \ - handle->name##_prev->name##_next = handle->name##_next; \ - } \ - if (handle->name##_next) { \ - handle->name##_next->name##_prev = handle->name##_prev; \ - } \ - \ - handle->flags &= ~UV_HANDLE_ACTIVE; \ - \ - return 0; \ - } \ - \ - \ - static void uv_##name##_invoke() { \ - uv_##name##_t* handle; \ - \ - uv_next_##name##_handle_ = uv_##name##_handles_; \ - \ - while (uv_next_##name##_handle_ != NULL) { \ - handle = uv_next_##name##_handle_; \ - uv_next_##name##_handle_ = handle->name##_next; \ - \ - handle->name##_cb(handle, 0); \ - } \ - } - - -UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) -UV_LOOP_WATCHER_DEFINE(check, CHECK) -UV_LOOP_WATCHER_DEFINE(idle, IDLE) - - -int uv_is_active(uv_handle_t* handle) { - switch (handle->type) { - case UV_TIMER: - case UV_IDLE: - case UV_PREPARE: - case UV_CHECK: - return (handle->flags & UV_HANDLE_ACTIVE) ? 1 : 0; - - default: - return 1; - } -} - - -int uv_async_init(uv_async_t* handle, uv_async_cb async_cb) { - uv_req_t* req; - - uv_counters()->handle_init++; - uv_counters()->async_init++; - - handle->type = UV_ASYNC; - handle->flags = 0; - handle->async_sent = 0; - handle->error = uv_ok_; - - req = &handle->async_req; - uv_req_init(req, (uv_handle_t*)handle, async_cb); - req->type = UV_WAKEUP; - - uv_refs_++; - - return 0; -} - - -int uv_async_send(uv_async_t* handle) { - if (handle->type != UV_ASYNC) { - /* Can't set errno because that's not thread-safe. */ - return -1; - } - - /* The user should make sure never to call uv_async_send to a closing */ - /* or closed handle. */ - assert(!(handle->flags & UV_HANDLE_CLOSING)); - - if (!uv_atomic_exchange_set(&handle->async_sent)) { - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &handle->async_req.overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } - - return 0; -} - - -static void uv_async_return_req(uv_async_t* handle, uv_req_t* req) { - assert(handle->type == UV_ASYNC); - assert(req->type == UV_WAKEUP); - - handle->async_sent = 0; - if (req->cb) { - ((uv_async_cb)req->cb)((uv_async_t*) handle, 0); - } - if (handle->flags & UV_HANDLE_CLOSING) { - uv_want_endgame((uv_handle_t*)handle); - } -} - - -static void uv_process_reqs() { - uv_req_t* req; - uv_handle_t* handle; - - while (req = uv_remove_pending_req()) { - handle = req->handle; - - switch (handle->type) { - case UV_TCP: - uv_tcp_return_req((uv_tcp_t*)handle, req); - break; - - case UV_ASYNC: - uv_async_return_req((uv_async_t*)handle, req); - break; - - case UV_ARES: - uv_ares_process((uv_ares_action_t*)handle, req); - break; - - case UV_ARES_TASK: - uv_ares_task_cleanup((uv_ares_task_t*)handle, req); - break; - - case UV_GETADDRINFO: - uv_getaddrinfo_done((uv_getaddrinfo_t*)handle, req); - break; - - default: - assert(0); - } - } -} - - -static void uv_process_timers() { - uv_timer_t* timer; - - /* Call timer callbacks */ - for (timer = RB_MIN(uv_timer_tree_s, &uv_timers_); - timer != NULL && timer->due <= uv_now_; - timer = RB_MIN(uv_timer_tree_s, &uv_timers_)) { - RB_REMOVE(uv_timer_tree_s, &uv_timers_, timer); - - if (timer->repeat != 0) { - /* If it is a repeating timer, reschedule with repeat timeout. */ - timer->due += timer->repeat; - if (timer->due < uv_now_) { - timer->due = uv_now_; - } - if (RB_INSERT(uv_timer_tree_s, &uv_timers_, timer) != NULL) { - uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); - } - } else { - /* If non-repeating, mark the timer as inactive. */ - timer->flags &= ~UV_HANDLE_ACTIVE; - } - - timer->timer_cb((uv_timer_t*) timer, 0); - } -} - - -static DWORD uv_get_poll_timeout() { - uv_timer_t* timer; - int64_t delta; - - /* Check if there are any running timers */ - timer = RB_MIN(uv_timer_tree_s, &uv_timers_); - if (timer) { - uv_update_time(); - - delta = timer->due - uv_now_; - if (delta >= UINT_MAX) { - /* Can't have a timeout greater than UINT_MAX, and a timeout value of */ - /* UINT_MAX means infinite, so that's no good either. */ - return UINT_MAX - 1; - } else if (delta < 0) { - /* Negative timeout values are not allowed */ - return 0; - } else { - return (DWORD)delta; - } - } else { - /* No timers */ - return INFINITE; - } -} - - -static void uv_poll() { - BOOL success; - DWORD bytes; - ULONG_PTR key; - OVERLAPPED* overlapped; - uv_req_t* req; - - success = GetQueuedCompletionStatus(uv_iocp_, - &bytes, - &key, - &overlapped, - uv_get_poll_timeout()); - - uv_update_time(); - - if (overlapped) { - /* Package was dequeued */ - req = uv_overlapped_to_req(overlapped); - - if (success) { - req->error = uv_ok_; - } else { - req->error = uv_new_sys_error(GetLastError()); - } - - uv_insert_pending_req(req); - - } else if (GetLastError() != WAIT_TIMEOUT) { - /* Serious error */ - uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); - } -} - - -int uv_run() { - while (1) { - uv_update_time(); - uv_process_timers(); - - /* Terrible: please fix me! */ - while (uv_refs_ > 0 && - (uv_idle_handles_ || uv_pending_reqs_tail_ || uv_endgame_handles_)) { - /* Terrible: please fix me! */ - while (uv_pending_reqs_tail_ || uv_endgame_handles_) { - uv_process_endgames(); - uv_process_reqs(); - } - - /* Call idle callbacks */ - uv_idle_invoke(); - } - - if (uv_refs_ <= 0) { - break; - } - - uv_prepare_invoke(); - - uv_poll(); - - uv_check_invoke(); - } - - assert(uv_refs_ == 0); - return 0; -} - - -void uv_ref() { - uv_refs_++; -} - - -void uv_unref() { - uv_refs_--; -} - - -int uv_utf16_to_utf8(wchar_t* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size) { - return WideCharToMultiByte(CP_UTF8, 0, utf16Buffer, utf16Size, utf8Buffer, utf8Size, NULL, NULL); -} - -int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, size_t utf16Size) { - return MultiByteToWideChar(CP_UTF8, 0, utf8Buffer, -1, utf16Buffer, utf16Size); -} - - -int uv_exepath(char* buffer, size_t* size) { - int retVal; - size_t utf16Size; - wchar_t* utf16Buffer; - - if (!buffer || !size) { - return -1; - } - - utf16Buffer = (wchar_t*)malloc(sizeof(wchar_t) * *size); - if (!utf16Buffer) { - retVal = -1; - goto done; - } - - /* Get the path as UTF-16 */ - utf16Size = GetModuleFileNameW(NULL, utf16Buffer, *size - 1); - if (utf16Size <= 0) { - uv_set_sys_error(GetLastError()); - retVal = -1; - goto done; - } - - utf16Buffer[utf16Size] = L'\0'; - - /* Convert to UTF-8 */ - *size = uv_utf16_to_utf8(utf16Buffer, utf16Size, buffer, *size); - if (!*size) { - uv_set_sys_error(GetLastError()); - retVal = -1; - goto done; - } - - buffer[*size] = '\0'; - retVal = 0; - -done: - if (utf16Buffer) { - free(utf16Buffer); - } - - return retVal; -} - - -uint64_t uv_hrtime(void) { - assert(0 && "implement me"); -} - - -/* thread pool callback when socket is signalled */ -VOID CALLBACK uv_ares_socksignal_tp(void* parameter, BOOLEAN timerfired) { - WSANETWORKEVENTS network_events; - uv_ares_task_t* sockhandle; - uv_ares_action_t* selhandle; - uv_req_t* uv_ares_req; - - assert(parameter != NULL); - - if (parameter != NULL) { - sockhandle = (uv_ares_task_t*)parameter; - - /* clear socket status for this event */ - /* do not fail if error, thread may run after socket close */ - /* The code assumes that c-ares will write all pending data in the callback, - unless the socket would block. We can clear the state here to avoid unecessary - signals. */ - WSAEnumNetworkEvents(sockhandle->sock, sockhandle->h_event, &network_events); - - /* setup new handle */ - selhandle = (uv_ares_action_t*)malloc(sizeof(uv_ares_action_t)); - if (selhandle == NULL) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - } - selhandle->type = UV_ARES; - selhandle->close_cb = NULL; - selhandle->data = sockhandle->data; - selhandle->sock = sockhandle->sock; - selhandle->read = (network_events.lNetworkEvents & (FD_READ | FD_CONNECT)) ? 1 : 0; - selhandle->write = (network_events.lNetworkEvents & (FD_WRITE | FD_CONNECT)) ? 1 : 0; - - uv_ares_req = &selhandle->ares_req; - uv_req_init(uv_ares_req, (uv_handle_t*)selhandle, NULL); - uv_ares_req->type = UV_WAKEUP; - - /* post ares needs to called */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &uv_ares_req->overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } -} - -/* callback from ares when socket operation is started */ -void uv_ares_sockstate_cb(void *data, ares_socket_t sock, int read, int write) { - /* look to see if we have a handle for this socket in our list */ - uv_ares_task_t* uv_handle_ares = uv_find_ares_handle(sock); - uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)data; - - struct timeval tv; - struct timeval* tvptr; - int timeoutms = 0; - - if (read == 0 && write == 0) { - /* if read and write are 0, cleanup existing data */ - /* The code assumes that c-ares does a callback with read = 0 and write = 0 - when the socket is closed. After we recieve this we stop monitoring the socket. */ - if (uv_handle_ares != NULL) { - uv_req_t* uv_ares_req; - - uv_handle_ares->h_close_event = CreateEvent(NULL, FALSE, FALSE, NULL); - /* remove Wait */ - if (uv_handle_ares->h_wait) { - UnregisterWaitEx(uv_handle_ares->h_wait, uv_handle_ares->h_close_event); - uv_handle_ares->h_wait = NULL; - } - - /* detach socket from the event */ - WSAEventSelect(sock, NULL, 0); - if (uv_handle_ares->h_event != WSA_INVALID_EVENT) { - WSACloseEvent(uv_handle_ares->h_event); - uv_handle_ares->h_event = WSA_INVALID_EVENT; - } - /* remove handle from list */ - uv_remove_ares_handle(uv_handle_ares); - - /* Post request to cleanup the Task */ - uv_ares_req = &uv_handle_ares->ares_req; - uv_req_init(uv_ares_req, (uv_handle_t*)uv_handle_ares, NULL); - uv_ares_req->type = UV_WAKEUP; - - /* post ares done with socket - finish cleanup when all threads done. */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &uv_ares_req->overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } else { - assert(0); - uv_fatal_error(ERROR_INVALID_DATA, "ares_SockStateCB"); - } - } else { - if (uv_handle_ares == NULL) { - /* setup new handle */ - /* The code assumes that c-ares will call us when it has an open socket. - We need to call into c-ares when there is something to read, - or when it becomes writable. */ - uv_handle_ares = (uv_ares_task_t*)malloc(sizeof(uv_ares_task_t)); - if (uv_handle_ares == NULL) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - } - uv_handle_ares->type = UV_ARES_TASK; - uv_handle_ares->close_cb = NULL; - uv_handle_ares->data = uv_ares_data_ptr; - uv_handle_ares->sock = sock; - uv_handle_ares->h_wait = NULL; - uv_handle_ares->flags = 0; - - /* create an event to wait on socket signal */ - uv_handle_ares->h_event = WSACreateEvent(); - if (uv_handle_ares->h_event == WSA_INVALID_EVENT) { - uv_fatal_error(WSAGetLastError(), "WSACreateEvent"); - } - - /* tie event to socket */ - if (SOCKET_ERROR == WSAEventSelect(sock, uv_handle_ares->h_event, FD_READ | FD_WRITE | FD_CONNECT)) { - uv_fatal_error(WSAGetLastError(), "WSAEventSelect"); - } - - /* add handle to list */ - uv_add_ares_handle(uv_handle_ares); - uv_refs_++; - - /* - * we have a single polling timer for all ares sockets. - * This is preferred to using ares_timeout. See ares_timeout.c warning. - * if timer is not running start it, and keep socket count - */ - if (uv_ares_data_ptr->activesockets == 0) { - uv_timer_init(&uv_ares_data_ptr->pollingtimer); - uv_timer_start(&uv_ares_data_ptr->pollingtimer, uv_ares_poll, 1000L, 1000L); - } - uv_ares_data_ptr->activesockets++; - - /* specify thread pool function to call when event is signaled */ - if (RegisterWaitForSingleObject(&uv_handle_ares->h_wait, - uv_handle_ares->h_event, - uv_ares_socksignal_tp, - (void*)uv_handle_ares, - INFINITE, - WT_EXECUTEINWAITTHREAD) == 0) { - uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); - } - } else { - /* found existing handle. */ - assert(uv_handle_ares->type == UV_ARES_TASK); - assert(uv_handle_ares->data != NULL); - assert(uv_handle_ares->h_event != WSA_INVALID_EVENT); - } - } -} - -/* called via uv_poll when ares completion port signaled */ -void uv_ares_process(uv_ares_action_t* handle, uv_req_t* req) { - uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)handle->data; - - ares_process_fd(uv_ares_data_ptr->channel, - handle->read ? handle->sock : INVALID_SOCKET, - handle->write ? handle->sock : INVALID_SOCKET); - - /* release handle for select here */ - free(handle); -} - -/* called via uv_poll when ares is finished with socket */ -void uv_ares_task_cleanup(uv_ares_task_t* handle, uv_req_t* req) { - /* check for event complete without waiting */ - unsigned int signaled = WaitForSingleObject(handle->h_close_event, 0); - - if (signaled != WAIT_TIMEOUT) { - uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)handle->data; - - uv_refs_--; - - /* close event handle and free uv handle memory */ - CloseHandle(handle->h_close_event); - free(handle); - - /* decrement active count. if it becomes 0 stop polling */ - if (uv_ares_data_ptr->activesockets > 0) { - uv_ares_data_ptr->activesockets--; - if (uv_ares_data_ptr->activesockets == 0) { - uv_close((uv_handle_t*)&uv_ares_data_ptr->pollingtimer, NULL); - } - } - } else { - /* stil busy - repost and try again */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &req->overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } -} - -/* periodically call ares to check for timeouts */ -void uv_ares_poll(uv_timer_t* handle, int status) { - if (uv_ares_data.channel != NULL && uv_ares_data.activesockets > 0) { - ares_process_fd(uv_ares_data.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); - } -} - - -/* set ares SOCK_STATE callback to our handler */ -int uv_ares_init_options(ares_channel *channelptr, - struct ares_options *options, - int optmask) { - int rc; - - /* only allow single init at a time */ - if (uv_ares_data.channel != NULL) { - return UV_EALREADY; - } - - /* set our callback as an option */ - options->sock_state_cb = uv_ares_sockstate_cb; - options->sock_state_cb_data = &uv_ares_data; - optmask |= ARES_OPT_SOCK_STATE_CB; - - /* We do the call to ares_init_option for caller. */ - rc = ares_init_options(channelptr, options, optmask); - - /* if success, save channel */ - if (rc == ARES_SUCCESS) { - uv_ares_data.channel = *channelptr; - } - - return rc; -} - - -/* release memory */ -void uv_ares_destroy(ares_channel channel) { - /* only allow destroy if did init */ - if (uv_ares_data.channel != NULL) { - ares_destroy(channel); - uv_ares_data.channel = NULL; - } -} - - -/* - * getaddrinfo error code mapping - * Falls back to uv_translate_sys_error if no match - */ - -static uv_err_code uv_translate_eai_error(int eai_errno) { - switch (eai_errno) { - case ERROR_SUCCESS: return UV_OK; - case EAI_BADFLAGS: return UV_EBADF; - case EAI_FAIL: return UV_EFAULT; - case EAI_FAMILY: return UV_EAIFAMNOSUPPORT; - case EAI_MEMORY: return UV_ENOMEM; - case EAI_NONAME: return UV_EAINONAME; - case EAI_AGAIN: return UV_EAGAIN; - case EAI_SERVICE: return UV_EAISERVICE; - case EAI_SOCKTYPE: return UV_EAISOCKTYPE; - default: return uv_translate_sys_error(eai_errno); - } -} - - -/* getaddrinfo worker thread implementation */ -static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) { - uv_getaddrinfo_t* handle = (uv_getaddrinfo_t*)parameter; - int ret; - - assert(handle != NULL); - - if (handle != NULL) { - /* call OS function on this thread */ - ret = GetAddrInfoW(handle->node, handle->service, handle->hints, &handle->res); - handle->retcode = ret; - - /* post getaddrinfo completed */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &handle->getadddrinfo_req.overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } - - return 0; -} - - -/* - * Called from uv_run when complete. Call user specified callback - * then free returned addrinfo - * Returned addrinfo strings are converted from UTF-16 to UTF-8. - * - * To minimize allocation we calculate total size required, - * and copy all structs and referenced strings into the one block. - * Each size calculation is adjusted to avoid unaligned pointers. - */ -static void uv_getaddrinfo_done(uv_getaddrinfo_t* handle, uv_req_t* req) { - int addrinfo_len = 0; - int name_len = 0; - size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo)); - struct addrinfoW* addrinfow_ptr; - struct addrinfo* addrinfo_ptr; - char* alloc_ptr = NULL; - char* cur_ptr = NULL; - uv_err_code uv_ret; - - /* release input parameter memory */ - if (handle->alloc != NULL) { - free(handle->alloc); - handle->alloc = NULL; - } - - uv_ret = uv_translate_eai_error(handle->retcode); - if (handle->retcode == 0) { - /* convert addrinfoW to addrinfo */ - /* first calculate required length */ - addrinfow_ptr = handle->res; - while (addrinfow_ptr != NULL) { - addrinfo_len += addrinfo_struct_len + ALIGNED_SIZE(addrinfow_ptr->ai_addrlen); - if (addrinfow_ptr->ai_canonname != NULL) { - name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); - if (name_len == 0) { - uv_ret = uv_translate_sys_error(GetLastError()); - goto complete; - } - addrinfo_len += ALIGNED_SIZE(name_len); - } - addrinfow_ptr = addrinfow_ptr->ai_next; - } - - /* allocate memory for addrinfo results */ - alloc_ptr = (char*)malloc(addrinfo_len); - - /* do conversions */ - if (alloc_ptr != NULL) { - cur_ptr = alloc_ptr; - addrinfow_ptr = handle->res; - - while (addrinfow_ptr != NULL) { - /* copy addrinfo struct data */ - assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len); - addrinfo_ptr = (struct addrinfo*)cur_ptr; - addrinfo_ptr->ai_family = addrinfow_ptr->ai_family; - addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype; - addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol; - addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags; - addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen; - addrinfo_ptr->ai_canonname = NULL; - addrinfo_ptr->ai_addr = NULL; - addrinfo_ptr->ai_next = NULL; - - cur_ptr += addrinfo_struct_len; - - /* copy sockaddr */ - if (addrinfo_ptr->ai_addrlen > 0) { - assert(cur_ptr + addrinfo_ptr->ai_addrlen <= alloc_ptr + addrinfo_len); - memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen); - addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr; - cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen); - } - - /* convert canonical name to UTF-8 */ - if (addrinfow_ptr->ai_canonname != NULL) { - name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); - assert(name_len > 0); - assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); - name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, cur_ptr, name_len); - assert(name_len > 0); - addrinfo_ptr->ai_canonname = cur_ptr; - cur_ptr += ALIGNED_SIZE(name_len); - } - assert(cur_ptr <= alloc_ptr + addrinfo_len); - - /* set next ptr */ - addrinfow_ptr = addrinfow_ptr->ai_next; - if (addrinfow_ptr != NULL) { - addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr; - } - } - } else { - uv_ret = UV_ENOMEM; - } - - } - - /* return memory to system */ - if (handle->res != NULL) { - FreeAddrInfoW(handle->res); - handle->res = NULL; - } - -complete: - /* finally do callback with converted result */ - handle->getaddrinfo_cb(handle, uv_ret, (struct addrinfo*)alloc_ptr); - - /* release copied result memory */ - if (alloc_ptr != NULL) { - free(alloc_ptr); - } - - uv_refs_--; -} - - -/* - * Entry point for getaddrinfo - * we convert the UTF-8 strings to UNICODE - * and save the UNICODE string pointers in the handle - * We also copy hints so that caller does not need to keep memory until the callback. - * return UV_OK if a callback will be made - * return error code if validation fails - * - * To minimize allocation we calculate total size required, - * and copy all structs and referenced strings into the one block. - * Each size calculation is adjusted to avoid unaligned pointers. - */ -int uv_getaddrinfo(uv_getaddrinfo_t* handle, - uv_getaddrinfo_cb getaddrinfo_cb, - const char* node, - const char* service, - const struct addrinfo* hints) { - int nodesize = 0; - int servicesize = 0; - int hintssize = 0; - char* alloc_ptr = NULL; - - if (handle == NULL || getaddrinfo_cb == NULL || - (node == NULL && service == NULL)) { - uv_set_sys_error(WSAEINVAL); - goto error; - } - - handle->getaddrinfo_cb = getaddrinfo_cb; - handle->res = NULL; - handle->type = UV_GETADDRINFO; - - /* calculate required memory size for all input values */ - if (node != NULL) { - nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(wchar_t)); - if (nodesize == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - } - - if (service != NULL) { - servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) * sizeof(wchar_t)); - if (servicesize == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - } - if (hints != NULL) { - hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW)); - } - - /* allocate memory for inputs, and partition it as needed */ - alloc_ptr = (char*)malloc(nodesize + servicesize + hintssize); - if (!alloc_ptr) { - uv_set_sys_error(WSAENOBUFS); - goto error; - } - - /* save alloc_ptr now so we can free if error */ - handle->alloc = (void*)alloc_ptr; - - /* convert node string to UTF16 into allocated memory and save pointer in handle */ - if (node != NULL) { - handle->node = (wchar_t*)alloc_ptr; - if (uv_utf8_to_utf16(node, (wchar_t*)alloc_ptr, nodesize / sizeof(wchar_t)) == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - alloc_ptr += nodesize; - } else { - handle->node = NULL; - } - - /* convert service string to UTF16 into allocated memory and save pointer in handle */ - if (service != NULL) { - handle->service = (wchar_t*)alloc_ptr; - if (uv_utf8_to_utf16(service, (wchar_t*)alloc_ptr, servicesize / sizeof(wchar_t)) == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - alloc_ptr += servicesize; - } else { - handle->service = NULL; - } - - /* copy hints to allocated memory and save pointer in handle */ - if (hints != NULL) { - handle->hints = (struct addrinfoW*)alloc_ptr; - handle->hints->ai_family = hints->ai_family; - handle->hints->ai_socktype = hints->ai_socktype; - handle->hints->ai_protocol = hints->ai_protocol; - handle->hints->ai_flags = hints->ai_flags; - handle->hints->ai_addrlen = 0; - handle->hints->ai_canonname = NULL; - handle->hints->ai_addr = NULL; - handle->hints->ai_next = NULL; - } else { - handle->hints = NULL; - } - - /* init request for Post handling */ - uv_req_init(&handle->getadddrinfo_req, (uv_handle_t*)handle, NULL); - handle->getadddrinfo_req.type = UV_WAKEUP; - - /* Ask thread to run. Treat this as a long operation */ - if (QueueUserWorkItem(&getaddrinfo_thread_proc, handle, WT_EXECUTELONGFUNCTION) == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - - uv_refs_++; - - return 0; - -error: - if (handle != NULL && handle->alloc != NULL) { - free(handle->alloc); - } - return -1; -} - diff --git a/src/rt/libuv/src/win/async.c b/src/rt/libuv/src/win/async.c new file mode 100644 index 00000000000..ee5faea272c --- /dev/null +++ b/src/rt/libuv/src/win/async.c @@ -0,0 +1,127 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "internal.h" + + +/* Atomic set operation on char */ +#ifdef _MSC_VER /* MSVC */ + +/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */ +/* efficient than InterlockedExchange, but InterlockedExchange8 does not */ +/* exist, and interlocked operations on larger targets might require the */ +/* target to be aligned. */ +#pragma intrinsic(_InterlockedOr8) + +static char __declspec(inline) uv_atomic_exchange_set(char volatile* target) { + return _InterlockedOr8(target, 1); +} + +#else /* GCC */ + +/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ +static inline char uv_atomic_exchange_set(char volatile* target) { + const char one = 1; + char old_value; + __asm__ __volatile__ ("lock xchgb %0, %1\n\t" + : "=r"(old_value), "=m"(*target) + : "0"(one), "m"(*target) + : "memory"); + return old_value; +} + +#endif + + +void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING && + !handle->async_sent) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { + uv_req_t* req; + + loop->counters.handle_init++; + loop->counters.async_init++; + + handle->type = UV_ASYNC; + handle->loop = loop; + handle->flags = 0; + handle->async_sent = 0; + handle->async_cb = async_cb; + + req = &handle->async_req; + uv_req_init(loop, req); + req->type = UV_WAKEUP; + req->data = handle; + + uv_ref(loop); + + return 0; +} + + +int uv_async_send(uv_async_t* handle) { + uv_loop_t* loop = handle->loop; + + if (handle->type != UV_ASYNC) { + /* Can't set errno because that's not thread-safe. */ + return -1; + } + + /* The user should make sure never to call uv_async_send to a closing */ + /* or closed handle. */ + assert(!(handle->flags & UV_HANDLE_CLOSING)); + + if (!uv_atomic_exchange_set(&handle->async_sent)) { + POST_COMPLETION_FOR_REQ(loop, &handle->async_req); + } + + return 0; +} + + +void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, + uv_req_t* req) { + assert(handle->type == UV_ASYNC); + assert(req->type == UV_WAKEUP); + + handle->async_sent = 0; + if (handle->async_cb) { + handle->async_cb((uv_async_t*) handle, 0); + } + if (handle->flags & UV_HANDLE_CLOSING) { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} diff --git a/src/rt/libuv/src/win/cares.c b/src/rt/libuv/src/win/cares.c new file mode 100644 index 00000000000..f146c31a56a --- /dev/null +++ b/src/rt/libuv/src/win/cares.c @@ -0,0 +1,289 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* + * Subclass of uv_handle_t. Used for integration of c-ares. + */ +struct uv_ares_action_s { + UV_HANDLE_FIELDS + struct uv_req_s ares_req; + SOCKET sock; + int read; + int write; +}; + + +/* default timeout per socket request if ares does not specify value */ +/* use 20 sec */ +#define ARES_TIMEOUT_MS 20000 + + +/* thread pool callback when socket is signalled */ +static void CALLBACK uv_ares_socksignal_tp(void* parameter, + BOOLEAN timerfired) { + WSANETWORKEVENTS network_events; + uv_ares_task_t* sockhandle; + uv_ares_action_t* selhandle; + uv_req_t* uv_ares_req; + uv_loop_t* loop; + + assert(parameter != NULL); + + if (parameter != NULL) { + sockhandle = (uv_ares_task_t*) parameter; + loop = sockhandle->loop; + + /* clear socket status for this event */ + /* do not fail if error, thread may run after socket close */ + /* The code assumes that c-ares will write all pending data in the */ + /* callback, unless the socket would block. We can clear the state here */ + /* to avoid unecessary signals. */ + WSAEnumNetworkEvents(sockhandle->sock, + sockhandle->h_event, + &network_events); + + /* setup new handle */ + selhandle = (uv_ares_action_t*)malloc(sizeof(uv_ares_action_t)); + if (selhandle == NULL) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + selhandle->type = UV_ARES_EVENT; + selhandle->close_cb = NULL; + selhandle->data = sockhandle->data; + selhandle->sock = sockhandle->sock; + selhandle->read = + (network_events.lNetworkEvents & (FD_READ | FD_CONNECT)) ? 1 : 0; + selhandle->write = + (network_events.lNetworkEvents & (FD_WRITE | FD_CONNECT)) ? 1 : 0; + + uv_ares_req = &selhandle->ares_req; + uv_req_init(loop, uv_ares_req); + uv_ares_req->type = UV_ARES_EVENT_REQ; + uv_ares_req->data = selhandle; + + /* post ares needs to called */ + POST_COMPLETION_FOR_REQ(loop, uv_ares_req); + } +} + + +/* periodically call ares to check for timeouts */ +static void uv_ares_poll(uv_timer_t* handle, int status) { + uv_loop_t* loop = handle->loop; + if (loop->ares_chan != NULL && loop->ares_active_sockets > 0) { + ares_process_fd(loop->ares_chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + } +} + + +/* callback from ares when socket operation is started */ +static void uv_ares_sockstate_cb(void *data, ares_socket_t sock, int read, + int write) { + /* look to see if we have a handle for this socket in our list */ + uv_loop_t* loop = (uv_loop_t*) data; + uv_ares_task_t* uv_handle_ares = uv_find_ares_handle(loop, sock); + + int timeoutms = 0; + + if (read == 0 && write == 0) { + /* if read and write are 0, cleanup existing data */ + /* The code assumes that c-ares does a callback with read = 0 and */ + /* write = 0 when the socket is closed. After we recieve this we stop */ + /* monitoring the socket. */ + if (uv_handle_ares != NULL) { + uv_req_t* uv_ares_req; + + uv_handle_ares->h_close_event = CreateEvent(NULL, FALSE, FALSE, NULL); + /* remove Wait */ + if (uv_handle_ares->h_wait) { + UnregisterWaitEx(uv_handle_ares->h_wait, + uv_handle_ares->h_close_event); + uv_handle_ares->h_wait = NULL; + } + + /* detach socket from the event */ + WSAEventSelect(sock, NULL, 0); + if (uv_handle_ares->h_event != WSA_INVALID_EVENT) { + WSACloseEvent(uv_handle_ares->h_event); + uv_handle_ares->h_event = WSA_INVALID_EVENT; + } + /* remove handle from list */ + uv_remove_ares_handle(uv_handle_ares); + + /* Post request to cleanup the Task */ + uv_ares_req = &uv_handle_ares->ares_req; + uv_req_init(loop, uv_ares_req); + uv_ares_req->type = UV_ARES_CLEANUP_REQ; + uv_ares_req->data = uv_handle_ares; + + /* post ares done with socket - finish cleanup when all threads done. */ + POST_COMPLETION_FOR_REQ(loop, uv_ares_req); + } else { + assert(0); + uv_fatal_error(ERROR_INVALID_DATA, "ares_SockStateCB"); + } + } else { + if (uv_handle_ares == NULL) { + /* setup new handle */ + /* The code assumes that c-ares will call us when it has an open socket. + We need to call into c-ares when there is something to read, + or when it becomes writable. */ + uv_handle_ares = (uv_ares_task_t*)malloc(sizeof(uv_ares_task_t)); + if (uv_handle_ares == NULL) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + uv_handle_ares->type = UV_ARES_TASK; + uv_handle_ares->close_cb = NULL; + uv_handle_ares->data = loop; + uv_handle_ares->sock = sock; + uv_handle_ares->h_wait = NULL; + uv_handle_ares->flags = 0; + + /* create an event to wait on socket signal */ + uv_handle_ares->h_event = WSACreateEvent(); + if (uv_handle_ares->h_event == WSA_INVALID_EVENT) { + uv_fatal_error(WSAGetLastError(), "WSACreateEvent"); + } + + /* tie event to socket */ + if (SOCKET_ERROR == WSAEventSelect(sock, + uv_handle_ares->h_event, + FD_READ | FD_WRITE | FD_CONNECT)) { + uv_fatal_error(WSAGetLastError(), "WSAEventSelect"); + } + + /* add handle to list */ + uv_add_ares_handle(loop, uv_handle_ares); + uv_ref(loop); + + /* + * we have a single polling timer for all ares sockets. + * This is preferred to using ares_timeout. See ares_timeout.c warning. + * if timer is not running start it, and keep socket count + */ + if (loop->ares_active_sockets == 0) { + uv_timer_init(loop, &loop->ares_polling_timer); + uv_timer_start(&loop->ares_polling_timer, uv_ares_poll, 1000L, 1000L); + } + loop->ares_active_sockets++; + + /* specify thread pool function to call when event is signaled */ + if (RegisterWaitForSingleObject(&uv_handle_ares->h_wait, + uv_handle_ares->h_event, + uv_ares_socksignal_tp, + (void*)uv_handle_ares, + INFINITE, + WT_EXECUTEINWAITTHREAD) == 0) { + uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); + } + } else { + /* found existing handle. */ + assert(uv_handle_ares->type == UV_ARES_TASK); + assert(uv_handle_ares->data != NULL); + assert(uv_handle_ares->h_event != WSA_INVALID_EVENT); + } + } +} + + +/* called via uv_poll when ares completion port signaled */ +void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle, + uv_req_t* req) { + ares_process_fd(loop->ares_chan, + handle->read ? handle->sock : INVALID_SOCKET, + handle->write ? handle->sock : INVALID_SOCKET); + + /* release handle for select here */ + free(handle); +} + + +/* called via uv_poll when ares is finished with socket */ +void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle, + uv_req_t* req) { + /* check for event complete without waiting */ + unsigned int signaled = WaitForSingleObject(handle->h_close_event, 0); + + if (signaled != WAIT_TIMEOUT) { + uv_unref(loop); + + /* close event handle and free uv handle memory */ + CloseHandle(handle->h_close_event); + free(handle); + + /* decrement active count. if it becomes 0 stop polling */ + if (loop->ares_active_sockets > 0) { + loop->ares_active_sockets--; + if (loop->ares_active_sockets == 0) { + uv_close((uv_handle_t*) &loop->ares_polling_timer, NULL); + } + } + } else { + /* stil busy - repost and try again */ + POST_COMPLETION_FOR_REQ(loop, req); + } +} + + +/* set ares SOCK_STATE callback to our handler */ +int uv_ares_init_options(uv_loop_t* loop, + ares_channel *channelptr, + struct ares_options *options, + int optmask) { + int rc; + + /* only allow single init at a time */ + if (loop->ares_chan != NULL) { + return UV_EALREADY; + } + + /* set our callback as an option */ + options->sock_state_cb = uv_ares_sockstate_cb; + options->sock_state_cb_data = loop; + optmask |= ARES_OPT_SOCK_STATE_CB; + + /* We do the call to ares_init_option for caller. */ + rc = ares_init_options(channelptr, options, optmask); + + /* if success, save channel */ + if (rc == ARES_SUCCESS) { + loop->ares_chan = *channelptr; + } + + return rc; +} + + +/* release memory */ +void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) { + /* only allow destroy if did init */ + if (loop->ares_chan != NULL) { + ares_destroy(channel); + loop->ares_chan = NULL; + } +} diff --git a/src/rt/libuv/src/win/core.c b/src/rt/libuv/src/win/core.c new file mode 100644 index 00000000000..3211bbf2afd --- /dev/null +++ b/src/rt/libuv/src/win/core.c @@ -0,0 +1,218 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* The only event loop we support right now */ +static uv_loop_t uv_default_loop_; + +/* uv_once intialization guards */ +static uv_once_t uv_init_guard_ = UV_ONCE_INIT; +static uv_once_t uv_default_loop_init_guard_ = UV_ONCE_INIT; + + +static void uv_init(void) { + /* Initialize winsock */ + uv_winsock_init(); + + /* Fetch winapi function pointers */ + uv_winapi_init(); + + /* Initialize FS */ + uv_fs_init(); +} + + +static void uv_loop_init(uv_loop_t* loop) { + /* Create an I/O completion port */ + loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); + if (loop->iocp == NULL) { + uv_fatal_error(GetLastError(), "CreateIoCompletionPort"); + } + + loop->refs = 0; + + uv_update_time(loop); + + loop->pending_reqs_tail = NULL; + + loop->endgame_handles = NULL; + + RB_INIT(&loop->timers); + + loop->check_handles = NULL; + loop->prepare_handles = NULL; + loop->idle_handles = NULL; + + loop->next_prepare_handle = NULL; + loop->next_check_handle = NULL; + loop->next_idle_handle = NULL; + + loop->ares_active_sockets = 0; + loop->ares_chan = NULL; + + loop->last_error = uv_ok_; +} + + +static void uv_default_loop_init(void) { + /* Intialize libuv itself first */ + uv_once(&uv_init_guard_, uv_init); + + /* Initialize the main loop */ + uv_loop_init(&uv_default_loop_); +} + + +uv_loop_t* uv_default_loop() { + uv_once(&uv_default_loop_init_guard_, uv_default_loop_init); + return &uv_default_loop_; +} + + +void uv_ref(uv_loop_t* loop) { + loop->refs++; +} + + +void uv_unref(uv_loop_t* loop) { + loop->refs--; +} + + +static void uv_poll(uv_loop_t* loop, int block) { + BOOL success; + DWORD bytes, timeout; + ULONG_PTR key; + OVERLAPPED* overlapped; + uv_req_t* req; + + if (block) { + timeout = uv_get_poll_timeout(loop); + } else { + timeout = 0; + } + + success = GetQueuedCompletionStatus(loop->iocp, + &bytes, + &key, + &overlapped, + timeout); + + if (overlapped) { + /* Package was dequeued */ + req = uv_overlapped_to_req(overlapped); + + uv_insert_pending_req(loop, req); + + } else if (GetLastError() != WAIT_TIMEOUT) { + /* Serious error */ + uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); + } +} + + +static void uv_poll_ex(uv_loop_t* loop, int block) { + BOOL success; + DWORD timeout; + uv_req_t* req; + OVERLAPPED_ENTRY overlappeds[64]; + ULONG count; + ULONG i; + + if (block) { + timeout = uv_get_poll_timeout(loop); + } else { + timeout = 0; + } + + assert(pGetQueuedCompletionStatusEx); + + success = pGetQueuedCompletionStatusEx(loop->iocp, + overlappeds, + COUNTOF(overlappeds), + &count, + timeout, + FALSE); + if (success) { + for (i = 0; i < count; i++) { + /* Package was dequeued */ + req = uv_overlapped_to_req(overlappeds[i].lpOverlapped); + uv_insert_pending_req(loop, req); + } + } else if (GetLastError() != WAIT_TIMEOUT) { + /* Serious error */ + uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx"); + } +} + + +#define UV_LOOP(loop, poll) \ + while ((loop)->refs > 0) { \ + uv_update_time((loop)); \ + uv_process_timers((loop)); \ + \ + /* Call idle callbacks if nothing to do. */ \ + if ((loop)->pending_reqs_tail == NULL && \ + (loop)->endgame_handles == NULL) { \ + uv_idle_invoke((loop)); \ + } \ + \ + /* Completely flush all pending reqs and endgames. */ \ + /* We do even when we just called the idle callbacks because those may */ \ + /* have closed handles or started requests that short-circuited. */ \ + while ((loop)->pending_reqs_tail || (loop)->endgame_handles) { \ + uv_process_endgames((loop)); \ + uv_process_reqs((loop)); \ + } \ + \ + if ((loop)->refs <= 0) { \ + break; \ + } \ + \ + uv_prepare_invoke((loop)); \ + \ + poll((loop), (loop)->idle_handles == NULL && (loop)->refs > 0); \ + \ + uv_check_invoke((loop)); \ + } + + +int uv_run(uv_loop_t* loop) { + if (pGetQueuedCompletionStatusEx) { + UV_LOOP(loop, uv_poll_ex); + } else { + UV_LOOP(loop, uv_poll); + } + + assert(loop->refs == 0); + return 0; +} diff --git a/src/rt/libuv/src/win/error.c b/src/rt/libuv/src/win/error.c new file mode 100644 index 00000000000..0ed2e16abe2 --- /dev/null +++ b/src/rt/libuv/src/win/error.c @@ -0,0 +1,157 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +const uv_err_t uv_ok_ = { UV_OK, ERROR_SUCCESS }; + + +/* + * Display an error message and abort the event loop. + */ +void uv_fatal_error(const int errorno, const char* syscall) { + char* buf = NULL; + const char* errmsg; + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL); + + if (buf) { + errmsg = buf; + } else { + errmsg = "Unknown error"; + } + + /* FormatMessage messages include a newline character already, */ + /* so don't add another. */ + if (syscall) { + fprintf(stderr, "%s: (%d) %s", syscall, errorno, errmsg); + } else { + fprintf(stderr, "(%d) %s", errorno, errmsg); + } + + if (buf) { + LocalFree(buf); + } + + *((char*)NULL) = 0xff; /* Force debug break */ + abort(); +} + + +uv_err_t uv_last_error(uv_loop_t* loop) { + return loop->last_error; +} + + +/* TODO: thread safety */ +static char* last_err_str_ = NULL; + +char* uv_strerror(uv_err_t err) { + if (last_err_str_ != NULL) { + LocalFree(last_err_str_); + } + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err.sys_errno_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &last_err_str_, 0, + NULL); + + if (last_err_str_) { + return last_err_str_; + } else { + return "Unknown error"; + } +} + + +uv_err_code uv_translate_sys_error(int sys_errno) { + switch (sys_errno) { + case ERROR_SUCCESS: return UV_OK; + case ERROR_FILE_NOT_FOUND: return UV_ENOENT; + case ERROR_PATH_NOT_FOUND: return UV_ENOENT; + case ERROR_NOACCESS: return UV_EACCESS; + case WSAEACCES: return UV_EACCESS; + case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; + case WSAEADDRINUSE: return UV_EADDRINUSE; + case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; + case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT; + case WSAEWOULDBLOCK: return UV_EAGAIN; + case WSAEALREADY: return UV_EALREADY; + case ERROR_CONNECTION_ABORTED: return UV_ECONNABORTED; + case WSAECONNABORTED: return UV_ECONNABORTED; + case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED; + case WSAECONNREFUSED: return UV_ECONNREFUSED; + case WSAEFAULT: return UV_EFAULT; + case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH; + case WSAEHOSTUNREACH: return UV_EHOSTUNREACH; + case ERROR_INVALID_DATA: return UV_EINVAL; + case WSAEINVAL: return UV_EINVAL; + case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; + case WSAEMFILE: return UV_EMFILE; + case WSAEMSGSIZE: return UV_EMSGSIZE; + case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH; + case WSAENETUNREACH: return UV_ENETUNREACH; + case ERROR_OUTOFMEMORY: return UV_ENOMEM; + case ERROR_NOT_CONNECTED: return UV_ENOTCONN; + case WSAENOTCONN: return UV_ENOTCONN; + case ERROR_NOT_SUPPORTED: return UV_ENOTSUP; + case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL; + case ERROR_INVALID_FLAGS: return UV_EBADF; + case ERROR_INVALID_PARAMETER: return UV_EINVAL; + case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; + case ERROR_BROKEN_PIPE: return UV_EOF; + case ERROR_PIPE_BUSY: return UV_EBUSY; + case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT; + case ERROR_ALREADY_EXISTS: return UV_EEXIST; + default: return UV_UNKNOWN; + } +} + + +uv_err_t uv_new_sys_error(int sys_errno) { + uv_err_t e; + e.code = uv_translate_sys_error(sys_errno); + e.sys_errno_ = sys_errno; + return e; +} + + +void uv_set_sys_error(uv_loop_t* loop, int sys_errno) { + loop->last_error.code = uv_translate_sys_error(sys_errno); + loop->last_error.sys_errno_ = sys_errno; +} + + +void uv_set_error(uv_loop_t* loop, uv_err_code code, int sys_errno) { + loop->last_error.code = code; + loop->last_error.sys_errno_ = sys_errno; +} diff --git a/src/rt/libuv/src/win/fs-event.c b/src/rt/libuv/src/win/fs-event.c new file mode 100644 index 00000000000..a68dfe00def --- /dev/null +++ b/src/rt/libuv/src/win/fs-event.c @@ -0,0 +1,384 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "uv.h" +#include "internal.h" + + +const unsigned int uv_directory_watcher_buffer_size = 4096; + + +static void uv_fs_event_init_handle(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb) { + handle->type = UV_FS_EVENT; + handle->loop = loop; + handle->flags = 0; + handle->cb = cb; + handle->is_path_dir = 0; + handle->dir_handle = INVALID_HANDLE_VALUE; + handle->buffer = NULL; + handle->req_pending = 0; + handle->filew = NULL; + + uv_req_init(loop, (uv_req_t*)&handle->req); + handle->req.type = UV_FS_EVENT_REQ; + handle->req.data = (void*)handle; + + handle->filename = strdup(filename); + if (!handle->filename) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + loop->counters.handle_init++; + loop->counters.fs_event_init++; + + uv_ref(loop); +} + + +static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop, + uv_fs_event_t* handle) { + assert(handle->dir_handle != INVALID_HANDLE_VALUE); + assert(!handle->req_pending); + + memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped)); + if (!ReadDirectoryChangesW(handle->dir_handle, + handle->buffer, + uv_directory_watcher_buffer_size, + FALSE, + FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_LAST_ACCESS | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY, + NULL, + &handle->req.overlapped, + NULL)) { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(&handle->req, GetLastError()); + uv_insert_pending_req(loop, (uv_req_t*)&handle->req); + } + + handle->req_pending = 1; +} + + +static int uv_split_path(const wchar_t* filename, wchar_t** dir, + wchar_t** file) { + int len = wcslen(filename); + int i = len; + while (i > 0 && filename[--i] != '\\' && filename[i] != '/'); + + if (i == 0) { + *dir = (wchar_t*)malloc((MAX_PATH + 1) * sizeof(wchar_t)); + if (!*dir) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!GetCurrentDirectoryW(MAX_PATH, *dir)) { + free(*dir); + *dir = NULL; + return -1; + } + + *file = wcsdup(filename); + } else { + *dir = (wchar_t*)malloc((i + 1) * sizeof(wchar_t)); + if (!*dir) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + wcsncpy(*dir, filename, i); + (*dir)[i] = L'\0'; + + *file = (wchar_t*)malloc((len - i) * sizeof(wchar_t)); + if (!*file) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + wcsncpy(*file, filename + i + 1, len - i - 1); + (*file)[len - i - 1] = L'\0'; + } + + return 0; +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb) { + int name_size; + DWORD attr, last_error; + wchar_t* dir = NULL, *dir_to_watch, *filenamew; + + uv_fs_event_init_handle(loop, handle, filename, cb); + + /* Convert name to UTF16. */ + name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(wchar_t); + filenamew = (wchar_t*)malloc(name_size); + if (!filenamew) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf8_to_utf16(filename, filenamew, + name_size / sizeof(wchar_t))) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* Determine whether filename is a file or a directory. */ + attr = GetFileAttributesW(filenamew); + if (attr == INVALID_FILE_ATTRIBUTES) { + last_error = GetLastError(); + goto error; + } + + handle->is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; + + if (handle->is_path_dir) { + /* filename is a directory, so that's the directory that we will watch. */ + dir_to_watch = filenamew; + } else { + /* + * filename is a file. So we split filename into dir & file parts, and + * watch the dir directory. + */ + if (uv_split_path(filenamew, &dir, &handle->filew) != 0) { + last_error = GetLastError(); + goto error; + } + + dir_to_watch = dir; + } + + handle->dir_handle = CreateFileW(dir_to_watch, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_DELETE | + FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OVERLAPPED, + NULL); + + if (dir) { + free(dir); + dir = NULL; + } + + if (handle->dir_handle == INVALID_HANDLE_VALUE) { + last_error = GetLastError(); + goto error; + } + + if (CreateIoCompletionPort(handle->dir_handle, + loop->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + last_error = GetLastError(); + goto error; + } + + handle->buffer = (char*)_aligned_malloc(uv_directory_watcher_buffer_size, + sizeof(DWORD)); + if (!handle->buffer) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped)); + + if (!ReadDirectoryChangesW(handle->dir_handle, + handle->buffer, + uv_directory_watcher_buffer_size, + FALSE, + FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_LAST_ACCESS | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY, + NULL, + &handle->req.overlapped, + NULL)) { + last_error = GetLastError(); + goto error; + } + + handle->req_pending = 1; + return 0; + +error: + if (handle->filename) { + free(handle->filename); + handle->filename = NULL; + } + + if (handle->filew) { + free(handle->filew); + handle->filew = NULL; + } + + if (handle->dir_handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->dir_handle); + handle->dir_handle = INVALID_HANDLE_VALUE; + } + + if (handle->buffer) { + _aligned_free(handle->buffer); + handle->buffer = NULL; + } + + uv_set_sys_error(loop, last_error); + return -1; +} + + +void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, + uv_fs_event_t* handle) { + FILE_NOTIFY_INFORMATION* file_info; + char* filename = NULL; + int utf8size; + DWORD offset = 0; + + assert(req->type == UV_FS_EVENT_REQ); + assert(handle->req_pending); + handle->req_pending = 0; + + file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset); + + if (REQ_SUCCESS(req)) { + if (req->overlapped.InternalHigh > 0) { + do { + file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset); + + /* + * Fire the event only if we were asked to watch a directory, + * or if the filename filter matches. + */ + if (handle->is_path_dir || _wcsnicmp(handle->filew, file_info->FileName, + file_info->FileNameLength / sizeof(wchar_t)) == 0) { + + /* Convert the filename to utf8. */ + utf8size = uv_utf16_to_utf8(file_info->FileName, + file_info->FileNameLength / + sizeof(wchar_t), + NULL, + 0); + if (utf8size) { + filename = (char*)malloc(utf8size + 1); + if (!filename) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + utf8size = uv_utf16_to_utf8(file_info->FileName, + file_info->FileNameLength / + sizeof(wchar_t), + filename, + utf8size); + if (utf8size) { + filename[utf8size] = L'\0'; + } else { + free(filename); + filename = NULL; + } + } + + switch (file_info->Action) { + case FILE_ACTION_ADDED: + case FILE_ACTION_REMOVED: + case FILE_ACTION_RENAMED_OLD_NAME: + case FILE_ACTION_RENAMED_NEW_NAME: + handle->cb(handle, filename, UV_RENAME, 0); + break; + + case FILE_ACTION_MODIFIED: + handle->cb(handle, filename, UV_CHANGE, 0); + break; + } + + free(filename); + filename = NULL; + } + + offset = file_info->NextEntryOffset; + } while(offset); + } else { + handle->cb(handle, NULL, UV_CHANGE, 0); + } + } else { + loop->last_error = GET_REQ_UV_ERROR(req); + handle->cb(handle, NULL, 0, -1); + } + + if (!(handle->flags & UV_HANDLE_CLOSING)) { + uv_fs_event_queue_readdirchanges(loop, handle); + } else { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} + + +void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) { + if (handle->dir_handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->dir_handle); + handle->dir_handle = INVALID_HANDLE_VALUE; + } + + if (!handle->req_pending) { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} + + +void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING && + !handle->req_pending) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->buffer) { + _aligned_free(handle->buffer); + handle->buffer = NULL; + } + + if (handle->filew) { + free(handle->filew); + handle->filew = NULL; + } + + if (handle->filename) { + free(handle->filename); + handle->filename = NULL; + } + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} diff --git a/src/rt/libuv/src/win/fs.c b/src/rt/libuv/src/win/fs.c new file mode 100644 index 00000000000..93fa5eb36b5 --- /dev/null +++ b/src/rt/libuv/src/win/fs.c @@ -0,0 +1,1302 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uv.h" +#include "internal.h" + +#define UV_FS_ASYNC_QUEUED 0x0001 +#define UV_FS_FREE_ARG0 0x0002 +#define UV_FS_FREE_ARG1 0x0004 +#define UV_FS_FREE_PTR 0x0008 +#define UV_FS_CLEANEDUP 0x0010 +#define UV_FS_LAST_ERROR_SET 0x0020 + +#define STRDUP_ARG(req, i) \ + req->arg##i = (void*)strdup((const char*)req->arg##i); \ + if (!req->arg##i) { \ + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); \ + } \ + req->flags |= UV_FS_FREE_ARG##i; + +#define WRAP_REQ_ARGS1(req, a0) \ + req->arg0 = (void*)a0; + +#define WRAP_REQ_ARGS2(req, a0, a1) \ + WRAP_REQ_ARGS1(req, a0) \ + req->arg1 = (void*)a1; + +#define WRAP_REQ_ARGS3(req, a0, a1, a2) \ + WRAP_REQ_ARGS2(req, a0, a1) \ + req->arg2 = (void*)a2; + +#define WRAP_REQ_ARGS4(req, a0, a1, a2, a3) \ + WRAP_REQ_ARGS3(req, a0, a1, a2) \ + req->arg3 = (void*)a3; + +#define QUEUE_FS_TP_JOB(loop, req) \ + if (!QueueUserWorkItem(&uv_fs_thread_proc, \ + req, \ + WT_EXECUTELONGFUNCTION)) { \ + uv_set_sys_error((loop), GetLastError()); \ + return -1; \ + } \ + req->flags |= UV_FS_ASYNC_QUEUED; \ + uv_ref((loop)); + + +#define SET_UV_LAST_ERROR_FROM_REQ(req) \ + if (req->flags & UV_FS_LAST_ERROR_SET) { \ + uv_set_sys_error(req->loop, req->last_error); \ + } + +#define SET_REQ_LAST_ERROR(req, error) \ + req->last_error = error; \ + req->flags |= UV_FS_LAST_ERROR_SET; + +#define SET_REQ_RESULT(req, result_value) \ + req->result = (result_value); \ + if (req->result == -1) { \ + req->errorno = uv_translate_sys_error(_doserrno); \ + } + +#define SET_REQ_RESULT_WIN32_ERROR(req, sys_errno) \ + req->result = -1; \ + req->errorno = uv_translate_sys_error(sys_errno); \ + SET_REQ_LAST_ERROR(req, sys_errno); + + +void uv_fs_init() { + _fmode = _O_BINARY; +} + + +static void uv_fs_req_init_async(uv_loop_t* loop, uv_fs_t* req, + uv_fs_type fs_type, const char* path, uv_fs_cb cb) { + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_FS; + req->loop = loop; + req->flags = 0; + req->fs_type = fs_type; + req->cb = cb; + req->result = 0; + req->ptr = NULL; + req->path = path ? strdup(path) : NULL; + req->errorno = 0; + req->last_error = 0; + memset(&req->overlapped, 0, sizeof(req->overlapped)); +} + + +static void uv_fs_req_init_sync(uv_loop_t* loop, uv_fs_t* req, + uv_fs_type fs_type) { + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_FS; + req->loop = loop; + req->flags = 0; + req->fs_type = fs_type; + req->result = 0; + req->ptr = NULL; + req->path = NULL; + req->errorno = 0; +} + + +void fs__open(uv_fs_t* req, const char* path, int flags, int mode) { + DWORD access; + DWORD share; + DWORD disposition; + DWORD attributes; + HANDLE file; + int result, current_umask; + + /* Obtain the active umask. umask() never fails and returns the previous */ + /* umask. */ + current_umask = umask(0); + umask(current_umask); + + /* convert flags and mode to CreateFile parameters */ + switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { + case _O_RDONLY: + access = GENERIC_READ; + break; + case _O_WRONLY: + access = GENERIC_WRITE; + break; + case _O_RDWR: + access = GENERIC_READ | GENERIC_WRITE; + break; + default: + result = -1; + goto end; + } + + /* + * Here is where we deviate significantly from what CRT's _open() + * does. We indiscriminately use all the sharing modes, to match + * UNIX semantics. In particular, this ensures that the file can + * be deleted even whilst it's open, fixing issue #1449. + */ + share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + + switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) { + case 0: + case _O_EXCL: + disposition = OPEN_EXISTING; + break; + case _O_CREAT: + disposition = OPEN_ALWAYS; + break; + case _O_CREAT | _O_EXCL: + case _O_CREAT | _O_TRUNC | _O_EXCL: + disposition = CREATE_NEW; + break; + case _O_TRUNC: + case _O_TRUNC | _O_EXCL: + disposition = TRUNCATE_EXISTING; + break; + case _O_CREAT | _O_TRUNC: + disposition = CREATE_ALWAYS; + break; + default: + result = -1; + goto end; + } + + attributes = FILE_ATTRIBUTE_NORMAL; + if (flags & _O_CREAT) { + if (!((mode & ~current_umask) & _S_IWRITE)) { + attributes |= FILE_ATTRIBUTE_READONLY; + } + } + + if (flags & _O_TEMPORARY ) { + attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY; + access |= DELETE; + } + + if (flags & _O_SHORT_LIVED) { + attributes |= FILE_ATTRIBUTE_TEMPORARY; + } + + switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) { + case 0: + break; + case _O_SEQUENTIAL: + attributes |= FILE_FLAG_SEQUENTIAL_SCAN; + break; + case _O_RANDOM: + attributes |= FILE_FLAG_RANDOM_ACCESS; + break; + default: + result = -1; + goto end; + } + + file = CreateFileA(path, + access, + share, + NULL, + disposition, + attributes, + NULL); + if (file == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + return; + } + result = _open_osfhandle((intptr_t)file, flags); +end: + SET_REQ_RESULT(req, result); +} + +void fs__close(uv_fs_t* req, uv_file file) { + int result = _close(file); + SET_REQ_RESULT(req, result); +} + + +void fs__read(uv_fs_t* req, uv_file file, void *buf, size_t length, + off_t offset) { + HANDLE handle; + OVERLAPPED overlapped, *overlapped_ptr; + LARGE_INTEGER offset_; + DWORD bytes; + + handle = (HANDLE) _get_osfhandle(file); + if (handle == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT(req, -1); + return; + } + + if (length > INT_MAX) { + SET_REQ_ERROR(req, ERROR_INSUFFICIENT_BUFFER); + return; + } + + if (offset != -1) { + memset(&overlapped, 0, sizeof overlapped); + + offset_.QuadPart = offset; + overlapped.Offset = offset_.LowPart; + overlapped.OffsetHigh = offset_.HighPart; + + overlapped_ptr = &overlapped; + } else { + overlapped_ptr = NULL; + } + + if (ReadFile(handle, buf, length, &bytes, overlapped_ptr)) { + SET_REQ_RESULT(req, bytes); + } else { + SET_REQ_ERROR(req, GetLastError()); + } +} + + +void fs__write(uv_fs_t* req, uv_file file, void *buf, size_t length, + off_t offset) { + HANDLE handle; + OVERLAPPED overlapped, *overlapped_ptr; + LARGE_INTEGER offset_; + DWORD bytes; + + handle = (HANDLE) _get_osfhandle(file); + if (handle == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT(req, -1); + return; + } + + if (length > INT_MAX) { + SET_REQ_ERROR(req, ERROR_INSUFFICIENT_BUFFER); + return; + } + + if (offset != -1) { + memset(&overlapped, 0, sizeof overlapped); + + offset_.QuadPart = offset; + overlapped.Offset = offset_.LowPart; + overlapped.OffsetHigh = offset_.HighPart; + + overlapped_ptr = &overlapped; + } else { + overlapped_ptr = NULL; + } + + if (WriteFile(handle, buf, length, &bytes, overlapped_ptr)) { + SET_REQ_RESULT(req, bytes); + } else { + SET_REQ_ERROR(req, GetLastError()); + } +} + + +void fs__unlink(uv_fs_t* req, const char* path) { + int result = _unlink(path); + SET_REQ_RESULT(req, result); +} + + +void fs__mkdir(uv_fs_t* req, const char* path, int mode) { + int result = _mkdir(path); + SET_REQ_RESULT(req, result); +} + + +void fs__rmdir(uv_fs_t* req, const char* path) { + int result = _rmdir(path); + SET_REQ_RESULT(req, result); +} + + +void fs__readdir(uv_fs_t* req, const char* path, int flags) { + int result; + char* buf, *ptr, *name; + HANDLE dir; + WIN32_FIND_DATAA ent = {0}; + size_t len = strlen(path); + size_t buf_size = 4096; + const char* fmt = !len ? "./*" + : (path[len - 1] == '/' || path[len - 1] == '\\') ? "%s*" + : "%s\\*"; + + char* path2 = (char*)malloc(len + 4); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + sprintf(path2, fmt, path); + dir = FindFirstFileA(path2, &ent); + free(path2); + + if(dir == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + return; + } + + buf = (char*)malloc(buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + ptr = buf; + result = 0; + + do { + name = ent.cFileName; + + if (name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))) { + len = strlen(name); + + while ((ptr - buf) + len + 1 > buf_size) { + buf_size *= 2; + path2 = buf; + buf = (char*)realloc(buf, buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "realloc"); + } + + ptr = buf + (ptr - path2); + } + + strcpy(ptr, name); + ptr += len + 1; + result++; + } + } while(FindNextFileA(dir, &ent)); + + FindClose(dir); + + req->ptr = buf; + req->flags |= UV_FS_FREE_PTR; + + SET_REQ_RESULT(req, result); +} + + +void fs__stat(uv_fs_t* req, const char* path) { + int result; + + result = _stati64(path, &req->stat); + if (result == -1) { + req->ptr = NULL; + } else { + req->ptr = &req->stat; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__fstat(uv_fs_t* req, uv_file file) { + int result; + + result = _fstati64(file, &req->stat); + if (result == -1) { + req->ptr = NULL; + } else { + req->ptr = &req->stat; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__rename(uv_fs_t* req, const char* path, const char* new_path) { + int result = rename(path, new_path); + SET_REQ_RESULT(req, result); +} + + +void fs__fsync(uv_fs_t* req, uv_file file) { + int result = FlushFileBuffers((HANDLE)_get_osfhandle(file)) ? 0 : -1; + if (result == -1) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + } else { + SET_REQ_RESULT(req, result); + } +} + + +void fs__ftruncate(uv_fs_t* req, uv_file file, off_t offset) { + int result = _chsize(file, offset); + SET_REQ_RESULT(req, result); +} + + +void fs__sendfile(uv_fs_t* req, uv_file out_file, uv_file in_file, + off_t in_offset, size_t length) { + const size_t max_buf_size = 65536; + size_t buf_size = length < max_buf_size ? length : max_buf_size; + int n, result = 0; + char* buf = (char*)malloc(buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (in_offset != -1) { + result = _lseek(in_file, in_offset, SEEK_SET); + } + + if (result != -1) { + while (length > 0) { + n = _read(in_file, buf, length < buf_size ? length : buf_size); + if (n == 0) { + break; + } else if (n == -1) { + result = -1; + break; + } + + length -= n; + + n = _write(out_file, buf, n); + if (n == -1) { + result = -1; + break; + } + + result += n; + } + } + + SET_REQ_RESULT(req, result); +} + + +void fs__chmod(uv_fs_t* req, const char* path, int mode) { + int result = _chmod(path, mode); + SET_REQ_RESULT(req, result); +} + + +void fs__fchmod(uv_fs_t* req, uv_file file, int mode) { + int result; + HANDLE handle; + NTSTATUS nt_status; + IO_STATUS_BLOCK io_status; + FILE_BASIC_INFORMATION file_info; + + handle = (HANDLE)_get_osfhandle(file); + + nt_status = pNtQueryInformationFile(handle, + &io_status, + &file_info, + sizeof file_info, + FileBasicInformation); + + if (nt_status != STATUS_SUCCESS) { + result = -1; + goto done; + } + + if (mode & _S_IWRITE) { + file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; + } else { + file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY; + } + + nt_status = pNtSetInformationFile(handle, + &io_status, + &file_info, + sizeof file_info, + FileBasicInformation); + + if (nt_status != STATUS_SUCCESS) { + result = -1; + goto done; + } + + result = 0; + +done: + SET_REQ_RESULT(req, result); +} + + +void fs__utime(uv_fs_t* req, const char* path, double atime, double mtime) { + int result; + struct _utimbuf b = {(time_t)atime, (time_t)mtime}; + result = _utime(path, &b); + SET_REQ_RESULT(req, result); +} + + +void fs__futime(uv_fs_t* req, uv_file file, double atime, double mtime) { + int result; + struct _utimbuf b = {(time_t)atime, (time_t)mtime}; + result = _futime(file, &b); + SET_REQ_RESULT(req, result); +} + + +void fs__link(uv_fs_t* req, const char* path, const char* new_path) { + int result = CreateHardLinkA(new_path, path, NULL) ? 0 : -1; + if (result == -1) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + } else { + SET_REQ_RESULT(req, result); + } +} + + +void fs__symlink(uv_fs_t* req, const char* path, const char* new_path, + int flags) { + int result; + if (pCreateSymbolicLinkA) { + result = pCreateSymbolicLinkA(new_path, + path, + flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1; + if (result == -1) { + SET_REQ_LAST_ERROR(req, GetLastError()); + } + } else { + result = -1; + errno = ENOSYS; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__readlink(uv_fs_t* req, const char* path) { + int result = -1; + BOOL rv; + HANDLE symlink; + void* buffer = NULL; + DWORD bytes_returned; + REPARSE_DATA_BUFFER* reparse_data; + int utf8size; + wchar_t* substitute_name; + int substitute_name_length; + + symlink = CreateFileA(path, + 0, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (INVALID_HANDLE_VALUE == symlink) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!buffer) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + rv = DeviceIoControl(symlink, + FSCTL_GET_REPARSE_POINT, + NULL, + 0, + buffer, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, + &bytes_returned, + NULL); + + if (!rv) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + reparse_data = buffer; + if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK) { + result = -1; + /* something is seriously wrong */ + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + substitute_name = reparse_data->SymbolicLinkReparseBuffer.PathBuffer + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)); + substitute_name_length = reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + + /* Strip off the leading \??\ from the substitute name buffer.*/ + if (memcmp(substitute_name, L"\\??\\", 8) == 0) { + substitute_name += 4; + substitute_name_length -= 4; + } + + utf8size = uv_utf16_to_utf8(substitute_name, + substitute_name_length, + NULL, + 0); + if (!utf8size) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + req->ptr = malloc(utf8size + 1); + if (!req->ptr) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + utf8size = uv_utf16_to_utf8(substitute_name, + substitute_name_length, + req->ptr, + utf8size); + if (!utf8size) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + req->flags |= UV_FS_FREE_PTR; + ((char*)req->ptr)[utf8size] = '\0'; + result = 0; + +done: + if (buffer) { + free(buffer); + } + + if (symlink != INVALID_HANDLE_VALUE) { + CloseHandle(symlink); + } + + SET_REQ_RESULT(req, result); +} + + +void fs__nop(uv_fs_t* req) { + req->result = 0; +} + + +static DWORD WINAPI uv_fs_thread_proc(void* parameter) { + uv_fs_t* req = (uv_fs_t*) parameter; + uv_loop_t* loop = req->loop; + + assert(req != NULL); + assert(req->type == UV_FS); + + switch (req->fs_type) { + case UV_FS_OPEN: + fs__open(req, req->path, (int)req->arg0, (int)req->arg1); + break; + case UV_FS_CLOSE: + fs__close(req, (uv_file)req->arg0); + break; + case UV_FS_READ: + fs__read(req, + (uv_file) req->arg0, + req->arg1, + (size_t) req->arg2, + (off_t) req->arg3); + break; + case UV_FS_WRITE: + fs__write(req, + (uv_file)req->arg0, + req->arg1, + (size_t) req->arg2, + (off_t) req->arg3); + break; + case UV_FS_UNLINK: + fs__unlink(req, req->path); + break; + case UV_FS_MKDIR: + fs__mkdir(req, req->path, (int)req->arg0); + break; + case UV_FS_RMDIR: + fs__rmdir(req, req->path); + break; + case UV_FS_READDIR: + fs__readdir(req, req->path, (int)req->arg0); + break; + case UV_FS_STAT: + case UV_FS_LSTAT: + fs__stat(req, req->path); + break; + case UV_FS_FSTAT: + fs__fstat(req, (uv_file)req->arg0); + break; + case UV_FS_RENAME: + fs__rename(req, req->path, (const char*)req->arg0); + break; + case UV_FS_FSYNC: + case UV_FS_FDATASYNC: + fs__fsync(req, (uv_file)req->arg0); + break; + case UV_FS_FTRUNCATE: + fs__ftruncate(req, (uv_file)req->arg0, (off_t)req->arg1); + break; + case UV_FS_SENDFILE: + fs__sendfile(req, + (uv_file) req->arg0, + (uv_file) req->arg1, + (off_t) req->arg2, + (size_t) req->arg3); + break; + case UV_FS_CHMOD: + fs__chmod(req, req->path, (int)req->arg0); + break; + case UV_FS_FCHMOD: + fs__fchmod(req, (uv_file)req->arg0, (int)req->arg1); + break; + case UV_FS_UTIME: + fs__utime(req, req->path, req->arg4, req->arg5); + break; + case UV_FS_FUTIME: + fs__futime(req, (uv_file)req->arg0, req->arg4, req->arg5); + break; + case UV_FS_LINK: + fs__link(req, req->path, (const char*)req->arg0); + break; + case UV_FS_SYMLINK: + fs__symlink(req, req->path, (const char*)req->arg0, (int)req->arg1); + break; + case UV_FS_READLINK: + fs__readlink(req, req->path); + break; + case UV_FS_CHOWN: + case UV_FS_FCHOWN: + fs__nop(req); + break; + default: + assert(!"bad uv_fs_type"); + } + + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + int mode, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_OPEN, path, cb); + WRAP_REQ_ARGS2(req, flags, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_OPEN); + fs__open(req, path, flags, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_CLOSE, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_CLOSE); + fs__close(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READ, NULL, cb); + WRAP_REQ_ARGS4(req, file, buf, length, offset); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READ); + fs__read(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_WRITE, NULL, cb); + WRAP_REQ_ARGS4(req, file, buf, length, offset); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_WRITE); + fs__write(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_UNLINK, path, cb); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_UNLINK); + fs__unlink(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_MKDIR, path, cb); + WRAP_REQ_ARGS1(req, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_MKDIR); + fs__mkdir(req, path, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_RMDIR, path, cb); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_RMDIR); + fs__rmdir(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READDIR, path, cb); + WRAP_REQ_ARGS1(req, flags); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READDIR); + fs__readdir(req, path, flags); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_LINK, path, cb); + WRAP_REQ_ARGS1(req, new_path); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_LINK); + fs__link(req, path, new_path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, int flags, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_SYMLINK, path, cb); + WRAP_REQ_ARGS2(req, new_path, flags); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_SYMLINK); + fs__symlink(req, path, new_path, flags); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READLINK, path, cb); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READLINK); + fs__readlink(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid, + int gid, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_CHOWN, path, cb); + WRAP_REQ_ARGS2(req, uid, gid); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_CHOWN); + fs__nop(req); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, + int gid, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FCHOWN, NULL, cb); + WRAP_REQ_ARGS3(req, file, uid, gid); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FCHOWN); + fs__nop(req); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + int len = strlen(path); + char* path2 = NULL; + int has_backslash = (path[len - 1] == '\\' || path[len - 1] == '/'); + + if (path[len - 1] == '\\' || path[len - 1] == '/') { + path2 = strdup(path); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + path2[len - 1] = '\0'; + } + + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_STAT, NULL, cb); + if (path2) { + req->path = path2; + } else { + req->path = strdup(path); + } + + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_STAT); + fs__stat(req, path2 ? path2 : path); + if (path2) { + free(path2); + } + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +/* TODO: add support for links. */ +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + int len = strlen(path); + char* path2 = NULL; + int has_backslash = (path[len - 1] == '\\' || path[len - 1] == '/'); + + if (path[len - 1] == '\\' || path[len - 1] == '/') { + path2 = strdup(path); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + path2[len - 1] = '\0'; + } + + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_LSTAT, NULL, cb); + if (path2) { + req->path = path2; + } else { + req->path = strdup(path); + } + + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_LSTAT); + fs__stat(req, path2 ? path2 : path); + if (path2) { + free(path2); + } + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FSTAT, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FSTAT); + fs__fstat(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_RENAME, path, cb); + WRAP_REQ_ARGS1(req, new_path); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_RENAME); + fs__rename(req, path, new_path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FDATASYNC, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FDATASYNC); + fs__fsync(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FSYNC, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FSYNC); + fs__fsync(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, + off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FTRUNCATE, NULL, cb); + WRAP_REQ_ARGS2(req, file, offset); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FTRUNCATE); + fs__ftruncate(req, file, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, + uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_SENDFILE, NULL, cb); + WRAP_REQ_ARGS4(req, out_fd, in_fd, in_offset, length); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_SENDFILE); + fs__sendfile(req, out_fd, in_fd, in_offset, length); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_CHMOD, path, cb); + WRAP_REQ_ARGS1(req, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_CHMOD); + fs__chmod(req, path, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FCHMOD, NULL, cb); + WRAP_REQ_ARGS2(req, file, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FCHMOD); + fs__fchmod(req, file, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_UTIME, path, cb); + req->arg4 = (ssize_t)atime; + req->arg5 = (ssize_t)mtime; + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_UTIME); + fs__utime(req, path, atime, mtime); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, + double mtime, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FUTIME, NULL, cb); + WRAP_REQ_ARGS1(req, file); + req->arg4 = (ssize_t)atime; + req->arg5 = (ssize_t)mtime; + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FUTIME); + fs__futime(req, file, atime, mtime); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req) { + assert(req->cb); + SET_UV_LAST_ERROR_FROM_REQ(req); + req->cb(req); +} + + +void uv_fs_req_cleanup(uv_fs_t* req) { + uv_loop_t* loop = req->loop; + + if (req->flags & UV_FS_CLEANEDUP) { + return; + } + + if (req->flags & UV_FS_FREE_ARG0 && req->arg0) { + free(req->arg0); + req->arg0 = NULL; + } + + if (req->flags & UV_FS_FREE_ARG1 && req->arg1) { + free(req->arg1); + req->arg1 = NULL; + } + + if (req->flags & UV_FS_FREE_PTR && req->ptr) { + free(req->ptr); + } + + req->ptr = NULL; + + if (req->path) { + free(req->path); + req->path = NULL; + } + + if (req->flags & UV_FS_ASYNC_QUEUED) { + uv_unref(loop); + } + + req->flags |= UV_FS_CLEANEDUP; +} diff --git a/src/rt/libuv/src/win/getaddrinfo.c b/src/rt/libuv/src/win/getaddrinfo.c new file mode 100644 index 00000000000..1416f04d62e --- /dev/null +++ b/src/rt/libuv/src/win/getaddrinfo.c @@ -0,0 +1,368 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include "uv.h" +#include "internal.h" + + +/* + * MinGW is missing this + */ +#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR) + typedef struct addrinfoW { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + wchar_t* ai_canonname; + struct sockaddr* ai_addr; + struct addrinfoW* ai_next; + } ADDRINFOW, *PADDRINFOW; + + DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const wchar_t* node, + const wchar_t* service, + const ADDRINFOW* hints, + PADDRINFOW* result); + + DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo); +#endif + + +/* adjust size value to be multiple of 4. Use to keep pointer aligned */ +/* Do we need different versions of this for different architectures? */ +#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) + + +/* + * getaddrinfo error code mapping + * Falls back to uv_translate_sys_error if no match + */ +static uv_err_code uv_translate_eai_error(int eai_errno) { + switch (eai_errno) { + case ERROR_SUCCESS: return UV_OK; + case EAI_BADFLAGS: return UV_EBADF; + case EAI_FAIL: return UV_EFAULT; + case EAI_FAMILY: return UV_EAIFAMNOSUPPORT; + case EAI_MEMORY: return UV_ENOMEM; + case EAI_NONAME: return UV_EAINONAME; + case EAI_AGAIN: return UV_EAGAIN; + case EAI_SERVICE: return UV_EAISERVICE; + case EAI_SOCKTYPE: return UV_EAISOCKTYPE; + default: return uv_translate_sys_error(eai_errno); + } +} + + +/* getaddrinfo worker thread implementation */ +static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) { + uv_getaddrinfo_t* handle = (uv_getaddrinfo_t*) parameter; + uv_loop_t* loop = handle->loop; + int ret; + + assert(handle != NULL); + + if (handle != NULL) { + /* call OS function on this thread */ + ret = GetAddrInfoW(handle->node, + handle->service, + handle->hints, + &handle->res); + handle->retcode = ret; + + /* post getaddrinfo completed */ + POST_COMPLETION_FOR_REQ(loop, &handle->getadddrinfo_req); + } + + return 0; +} + + +/* + * Called from uv_run when complete. Call user specified callback + * then free returned addrinfo + * Returned addrinfo strings are converted from UTF-16 to UTF-8. + * + * To minimize allocation we calculate total size required, + * and copy all structs and referenced strings into the one block. + * Each size calculation is adjusted to avoid unaligned pointers. + */ +void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* handle, + uv_req_t* req) { + int addrinfo_len = 0; + int name_len = 0; + size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo)); + struct addrinfoW* addrinfow_ptr; + struct addrinfo* addrinfo_ptr; + char* alloc_ptr = NULL; + char* cur_ptr = NULL; + uv_err_code uv_ret; + + /* release input parameter memory */ + if (handle->alloc != NULL) { + free(handle->alloc); + handle->alloc = NULL; + } + + uv_ret = uv_translate_eai_error(handle->retcode); + if (handle->retcode == 0) { + /* convert addrinfoW to addrinfo */ + /* first calculate required length */ + addrinfow_ptr = handle->res; + while (addrinfow_ptr != NULL) { + addrinfo_len += addrinfo_struct_len + + ALIGNED_SIZE(addrinfow_ptr->ai_addrlen); + if (addrinfow_ptr->ai_canonname != NULL) { + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); + if (name_len == 0) { + uv_ret = uv_translate_sys_error(GetLastError()); + goto complete; + } + addrinfo_len += ALIGNED_SIZE(name_len); + } + addrinfow_ptr = addrinfow_ptr->ai_next; + } + + /* allocate memory for addrinfo results */ + alloc_ptr = (char*)malloc(addrinfo_len); + + /* do conversions */ + if (alloc_ptr != NULL) { + cur_ptr = alloc_ptr; + addrinfow_ptr = handle->res; + + while (addrinfow_ptr != NULL) { + /* copy addrinfo struct data */ + assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len); + addrinfo_ptr = (struct addrinfo*)cur_ptr; + addrinfo_ptr->ai_family = addrinfow_ptr->ai_family; + addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype; + addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol; + addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags; + addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen; + addrinfo_ptr->ai_canonname = NULL; + addrinfo_ptr->ai_addr = NULL; + addrinfo_ptr->ai_next = NULL; + + cur_ptr += addrinfo_struct_len; + + /* copy sockaddr */ + if (addrinfo_ptr->ai_addrlen > 0) { + assert(cur_ptr + addrinfo_ptr->ai_addrlen <= + alloc_ptr + addrinfo_len); + memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen); + addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr; + cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen); + } + + /* convert canonical name to UTF-8 */ + if (addrinfow_ptr->ai_canonname != NULL) { + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, + -1, + NULL, + 0); + assert(name_len > 0); + assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, + -1, + cur_ptr, + name_len); + assert(name_len > 0); + addrinfo_ptr->ai_canonname = cur_ptr; + cur_ptr += ALIGNED_SIZE(name_len); + } + assert(cur_ptr <= alloc_ptr + addrinfo_len); + + /* set next ptr */ + addrinfow_ptr = addrinfow_ptr->ai_next; + if (addrinfow_ptr != NULL) { + addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr; + } + } + } else { + uv_ret = UV_ENOMEM; + } + + } + + /* return memory to system */ + if (handle->res != NULL) { + FreeAddrInfoW(handle->res); + handle->res = NULL; + } + +complete: + /* finally do callback with converted result */ + handle->getaddrinfo_cb(handle, uv_ret, (struct addrinfo*)alloc_ptr); + + uv_unref(loop); +} + + +void uv_freeaddrinfo(struct addrinfo* ai) { + char* alloc_ptr = (char*)ai; + + /* release copied result memory */ + if (alloc_ptr != NULL) { + free(alloc_ptr); + } +} + + +/* + * Entry point for getaddrinfo + * we convert the UTF-8 strings to UNICODE + * and save the UNICODE string pointers in the handle + * We also copy hints so that caller does not need to keep memory until the + * callback. + * return UV_OK if a callback will be made + * return error code if validation fails + * + * To minimize allocation we calculate total size required, + * and copy all structs and referenced strings into the one block. + * Each size calculation is adjusted to avoid unaligned pointers. + */ +int uv_getaddrinfo(uv_loop_t* loop, + uv_getaddrinfo_t* handle, + uv_getaddrinfo_cb getaddrinfo_cb, + const char* node, + const char* service, + const struct addrinfo* hints) { + int nodesize = 0; + int servicesize = 0; + int hintssize = 0; + char* alloc_ptr = NULL; + + if (handle == NULL || getaddrinfo_cb == NULL || + (node == NULL && service == NULL)) { + uv_set_sys_error(loop, WSAEINVAL); + goto error; + } + + uv_req_init(loop, (uv_req_t*)handle); + + handle->getaddrinfo_cb = getaddrinfo_cb; + handle->res = NULL; + handle->type = UV_GETADDRINFO; + handle->loop = loop; + + /* calculate required memory size for all input values */ + if (node != NULL) { + nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(wchar_t)); + if (nodesize == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + } + + if (service != NULL) { + servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) * + sizeof(wchar_t)); + if (servicesize == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + } + if (hints != NULL) { + hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW)); + } + + /* allocate memory for inputs, and partition it as needed */ + alloc_ptr = (char*)malloc(nodesize + servicesize + hintssize); + if (!alloc_ptr) { + uv_set_sys_error(loop, WSAENOBUFS); + goto error; + } + + /* save alloc_ptr now so we can free if error */ + handle->alloc = (void*)alloc_ptr; + + /* convert node string to UTF16 into allocated memory and save pointer in */ + /* handle */ + if (node != NULL) { + handle->node = (wchar_t*)alloc_ptr; + if (uv_utf8_to_utf16(node, + (wchar_t*) alloc_ptr, + nodesize / sizeof(wchar_t)) == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + alloc_ptr += nodesize; + } else { + handle->node = NULL; + } + + /* convert service string to UTF16 into allocated memory and save pointer */ + /* in handle */ + if (service != NULL) { + handle->service = (wchar_t*)alloc_ptr; + if (uv_utf8_to_utf16(service, + (wchar_t*) alloc_ptr, + servicesize / sizeof(wchar_t)) == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + alloc_ptr += servicesize; + } else { + handle->service = NULL; + } + + /* copy hints to allocated memory and save pointer in handle */ + if (hints != NULL) { + handle->hints = (struct addrinfoW*)alloc_ptr; + handle->hints->ai_family = hints->ai_family; + handle->hints->ai_socktype = hints->ai_socktype; + handle->hints->ai_protocol = hints->ai_protocol; + handle->hints->ai_flags = hints->ai_flags; + handle->hints->ai_addrlen = 0; + handle->hints->ai_canonname = NULL; + handle->hints->ai_addr = NULL; + handle->hints->ai_next = NULL; + } else { + handle->hints = NULL; + } + + /* init request for Post handling */ + uv_req_init(loop, &handle->getadddrinfo_req); + handle->getadddrinfo_req.data = handle; + handle->getadddrinfo_req.type = UV_GETADDRINFO_REQ; + + /* Ask thread to run. Treat this as a long operation */ + if (QueueUserWorkItem(&getaddrinfo_thread_proc, + handle, + WT_EXECUTELONGFUNCTION) == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + + uv_ref(loop); + + return 0; + +error: + if (handle != NULL && handle->alloc != NULL) { + free(handle->alloc); + } + return -1; +} diff --git a/src/rt/libuv/src/win/handle.c b/src/rt/libuv/src/win/handle.c new file mode 100644 index 00000000000..ab4f64bc5ae --- /dev/null +++ b/src/rt/libuv/src/win/handle.c @@ -0,0 +1,193 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "internal.h" + + +int uv_is_active(uv_handle_t* handle) { + switch (handle->type) { + case UV_TIMER: + case UV_IDLE: + case UV_PREPARE: + case UV_CHECK: + return (handle->flags & UV_HANDLE_ACTIVE) ? 1 : 0; + + default: + return 1; + } +} + + +void uv_close(uv_handle_t* handle, uv_close_cb cb) { + uv_tcp_t* tcp; + uv_pipe_t* pipe; + uv_udp_t* udp; + uv_process_t* process; + + uv_loop_t* loop = handle->loop; + + if (handle->flags & UV_HANDLE_CLOSING) { + return; + } + + handle->flags |= UV_HANDLE_CLOSING; + handle->close_cb = cb; + + /* Handle-specific close actions */ + switch (handle->type) { + case UV_TCP: + tcp = (uv_tcp_t*)handle; + /* If we don't shutdown before calling closesocket, windows will */ + /* silently discard the kernel send buffer and reset the connection. */ + if (!(tcp->flags & UV_HANDLE_SHUT)) { + shutdown(tcp->socket, SD_SEND); + tcp->flags |= UV_HANDLE_SHUT; + } + tcp->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); + closesocket(tcp->socket); + if (tcp->reqs_pending == 0) { + uv_want_endgame(loop, handle); + } + return; + + case UV_NAMED_PIPE: + pipe = (uv_pipe_t*)handle; + pipe->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); + close_pipe(pipe, NULL, NULL); + if (pipe->reqs_pending == 0) { + uv_want_endgame(loop, handle); + } + return; + + case UV_UDP: + udp = (uv_udp_t*) handle; + uv_udp_recv_stop(udp); + closesocket(udp->socket); + if (udp->reqs_pending == 0) { + uv_want_endgame(loop, handle); + } + return; + + case UV_TIMER: + uv_timer_stop((uv_timer_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_PREPARE: + uv_prepare_stop((uv_prepare_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_CHECK: + uv_check_stop((uv_check_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_IDLE: + uv_idle_stop((uv_idle_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_ASYNC: + if (!((uv_async_t*)handle)->async_sent) { + uv_want_endgame(loop, handle); + } + return; + + case UV_PROCESS: + process = (uv_process_t*)handle; + uv_process_close(loop, process); + return; + + case UV_FS_EVENT: + uv_fs_event_close(loop, (uv_fs_event_t*)handle); + return; + + default: + /* Not supported */ + abort(); + } +} + + +void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) { + if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) { + handle->flags |= UV_HANDLE_ENDGAME_QUEUED; + + handle->endgame_next = loop->endgame_handles; + loop->endgame_handles = handle; + } +} + + +void uv_process_endgames(uv_loop_t* loop) { + uv_handle_t* handle; + + while (loop->endgame_handles) { + handle = loop->endgame_handles; + loop->endgame_handles = handle->endgame_next; + + handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED; + + switch (handle->type) { + case UV_TCP: + uv_tcp_endgame(loop, (uv_tcp_t*) handle); + break; + + case UV_NAMED_PIPE: + uv_pipe_endgame(loop, (uv_pipe_t*) handle); + break; + + case UV_UDP: + uv_udp_endgame(loop, (uv_udp_t*) handle); + break; + + case UV_TIMER: + uv_timer_endgame(loop, (uv_timer_t*) handle); + break; + + case UV_PREPARE: + case UV_CHECK: + case UV_IDLE: + uv_loop_watcher_endgame(loop, handle); + break; + + case UV_ASYNC: + uv_async_endgame(loop, (uv_async_t*) handle); + break; + + case UV_PROCESS: + uv_process_endgame(loop, (uv_process_t*) handle); + break; + + case UV_FS_EVENT: + uv_fs_event_endgame(loop, (uv_fs_event_t*) handle); + break; + + default: + assert(0); + break; + } + } +} diff --git a/src/rt/libuv/src/win/internal.h b/src/rt/libuv/src/win/internal.h new file mode 100644 index 00000000000..87a64eda1b2 --- /dev/null +++ b/src/rt/libuv/src/win/internal.h @@ -0,0 +1,305 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_WIN_INTERNAL_H_ +#define UV_WIN_INTERNAL_H_ + +#include "uv.h" +#include "../uv-common.h" + +#include "tree.h" +#include "winapi.h" +#include "winsock.h" + + +/* + * Timers + */ +void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle); + +DWORD uv_get_poll_timeout(uv_loop_t* loop); +void uv_process_timers(uv_loop_t* loop); + + +/* + * Handles + */ + +/* Private uv_handle flags */ +#define UV_HANDLE_CLOSING 0x0001 +#define UV_HANDLE_CLOSED 0x0002 +#define UV_HANDLE_BOUND 0x0004 +#define UV_HANDLE_LISTENING 0x0008 +#define UV_HANDLE_CONNECTION 0x0010 +#define UV_HANDLE_CONNECTED 0x0020 +#define UV_HANDLE_READING 0x0040 +#define UV_HANDLE_ACTIVE 0x0040 +#define UV_HANDLE_EOF 0x0080 +#define UV_HANDLE_SHUTTING 0x0100 +#define UV_HANDLE_SHUT 0x0200 +#define UV_HANDLE_ENDGAME_QUEUED 0x0400 +#define UV_HANDLE_BIND_ERROR 0x1000 +#define UV_HANDLE_IPV6 0x2000 +#define UV_HANDLE_PIPESERVER 0x4000 +#define UV_HANDLE_READ_PENDING 0x8000 +#define UV_HANDLE_GIVEN_OS_HANDLE 0x10000 +#define UV_HANDLE_UV_ALLOCED 0x20000 +#define UV_HANDLE_SYNC_BYPASS_IOCP 0x40000 +#define UV_HANDLE_ZERO_READ 0x80000 + +void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle); +void uv_process_endgames(uv_loop_t* loop); + +#define DECREASE_PENDING_REQ_COUNT(handle) \ + do { \ + assert(handle->reqs_pending > 0); \ + handle->reqs_pending--; \ + \ + if (handle->flags & UV_HANDLE_CLOSING && \ + handle->reqs_pending == 0) { \ + uv_want_endgame(loop, (uv_handle_t*)handle); \ + } \ + } while (0) + +#define UV_SUCCEEDED_WITHOUT_IOCP(result) \ + ((result) && (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP)) + +#define UV_SUCCEEDED_WITH_IOCP(result) \ + ((result) || (GetLastError() == ERROR_IO_PENDING)) + + +/* + * Requests + */ +void uv_req_init(uv_loop_t* loop, uv_req_t* req); + +uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped); + +void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req); +void uv_process_reqs(uv_loop_t* loop); + +#define POST_COMPLETION_FOR_REQ(loop, req) \ + if (!PostQueuedCompletionStatus((loop)->iocp, \ + 0, \ + 0, \ + &((req)->overlapped))) { \ + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); \ + } + + +/* + * Streams + */ +void uv_stream_init(uv_loop_t* loop, uv_stream_t* handle); +void uv_connection_init(uv_stream_t* handle); + +size_t uv_count_bufs(uv_buf_t bufs[], int count); + + +/* + * TCP + */ +int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); +int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client); +int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb); +int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb); + +void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req); +void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, + uv_write_t* req); +void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, + uv_req_t* req); +void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, + uv_connect_t* req); + +void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); + + +/* + * UDP + */ +void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req); +void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, + uv_udp_send_t* req); + +void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle); + + +/* + * Pipes + */ +int uv_pipe_init_with_handle(uv_loop_t* loop, uv_pipe_t* handle, + HANDLE pipeHandle); +int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, + char* name, size_t nameSize); +void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err); +void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle); + +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); +int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client); +int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb); +int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb); + +void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_req_t* req); +void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_write_t* req); +void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_req_t* raw_req); +void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_connect_t* req); +void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_shutdown_t* req); + +/* + * Loop watchers + */ +void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle); + +void uv_prepare_invoke(uv_loop_t* loop); +void uv_check_invoke(uv_loop_t* loop); +void uv_idle_invoke(uv_loop_t* loop); + + +/* + * Async watcher + */ +void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle); + +void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, + uv_req_t* req); + + +/* + * Spawn + */ +void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle); +void uv_process_proc_close(uv_loop_t* loop, uv_process_t* handle); +void uv_process_close(uv_loop_t* loop, uv_process_t* handle); +void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle); + + +/* + * C-ares integration + */ +typedef struct uv_ares_action_s uv_ares_action_t; + +void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle, + uv_req_t* req); +void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle, + uv_req_t* req); + +/* + * Getaddrinfo + */ +void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* handle, + uv_req_t* req); + + +/* + * FS + */ +void uv_fs_init(); +void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req); + + +/* + * Threadpool + */ +void uv_process_work_req(uv_loop_t* loop, uv_work_t* req); + + +/* + * FS Event + */ +void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle); +void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle); +void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle); + + +/* + * Error handling + */ +extern const uv_err_t uv_ok_; + +void uv_fatal_error(const int errorno, const char* syscall); + +uv_err_code uv_translate_sys_error(int sys_errno); +uv_err_t uv_new_sys_error(int sys_errno); +void uv_set_sys_error(uv_loop_t* loop, int sys_errno); +void uv_set_error(uv_loop_t* loop, uv_err_code code, int sys_errno); + +#define SET_REQ_STATUS(req, status) \ + (req)->overlapped.Internal = (ULONG_PTR) (status) + +#define SET_REQ_ERROR(req, error) \ + SET_REQ_STATUS((req), NTSTATUS_FROM_WIN32((error))) + +#define SET_REQ_SUCCESS(req) \ + SET_REQ_STATUS((req), STATUS_SUCCESS) + +#define GET_REQ_STATUS(req) \ + ((req)->overlapped.Internal) + +#define REQ_SUCCESS(req) \ + (NT_SUCCESS(GET_REQ_STATUS((req)))) + +#define GET_REQ_ERROR(req) \ + (pRtlNtStatusToDosError(GET_REQ_STATUS((req)))) + +#define GET_REQ_SOCK_ERROR(req) \ + (uv_ntstatus_to_winsock_error(GET_REQ_STATUS((req)))) + +#define GET_REQ_UV_ERROR(req) \ + (uv_new_sys_error(GET_REQ_ERROR((req)))) + +#define GET_REQ_UV_SOCK_ERROR(req) \ + (uv_new_sys_error(GET_REQ_SOCK_ERROR((req)))) + + +/* + * Initialization for the windows and winsock api + */ +void uv_winapi_init(); +void uv_winsock_init(); +int uv_ntstatus_to_winsock_error(NTSTATUS status); + + +/* Threads and synchronization */ +typedef struct uv_once_s { + unsigned char ran; + /* The actual event handle must be aligned to sizeof(HANDLE), so in */ + /* practice it might overlap padding a little. */ + HANDLE event; + HANDLE padding; +} uv_once_t; + +#define UV_ONCE_INIT \ + { 0, NULL, NULL } + +void uv_once(uv_once_t* guard, void (*callback)(void)); + + +#endif /* UV_WIN_INTERNAL_H_ */ diff --git a/src/rt/libuv/src/win/loop-watcher.c b/src/rt/libuv/src/win/loop-watcher.c new file mode 100644 index 00000000000..c597cd99b12 --- /dev/null +++ b/src/rt/libuv/src/win/loop-watcher.c @@ -0,0 +1,131 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb(handle); + } + + uv_unref(loop); + } +} + + +#define UV_LOOP_WATCHER_DEFINE(name, NAME) \ + int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \ + handle->type = UV_##NAME; \ + handle->loop = loop; \ + handle->flags = 0; \ + \ + uv_ref(loop); \ + \ + loop->counters.handle_init++; \ + loop->counters.name##_init++; \ + \ + return 0; \ + } \ + \ + \ + int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ + uv_loop_t* loop = handle->loop; \ + uv_##name##_t* old_head; \ + \ + assert(handle->type == UV_##NAME); \ + \ + if (handle->flags & UV_HANDLE_ACTIVE) \ + return 0; \ + \ + old_head = loop->name##_handles; \ + \ + handle->name##_next = old_head; \ + handle->name##_prev = NULL; \ + \ + if (old_head) { \ + old_head->name##_prev = handle; \ + } \ + \ + loop->name##_handles = handle; \ + \ + handle->name##_cb = cb; \ + handle->flags |= UV_HANDLE_ACTIVE; \ + \ + return 0; \ + } \ + \ + \ + int uv_##name##_stop(uv_##name##_t* handle) { \ + uv_loop_t* loop = handle->loop; \ + \ + assert(handle->type == UV_##NAME); \ + \ + if (!(handle->flags & UV_HANDLE_ACTIVE)) \ + return 0; \ + \ + /* Update loop head if needed */ \ + if (loop->name##_handles == handle) { \ + loop->name##_handles = handle->name##_next; \ + } \ + \ + /* Update the iterator-next pointer of needed */ \ + if (loop->next_##name##_handle == handle) { \ + loop->next_##name##_handle = handle->name##_next; \ + } \ + \ + if (handle->name##_prev) { \ + handle->name##_prev->name##_next = handle->name##_next; \ + } \ + if (handle->name##_next) { \ + handle->name##_next->name##_prev = handle->name##_prev; \ + } \ + \ + handle->flags &= ~UV_HANDLE_ACTIVE; \ + \ + return 0; \ + } \ + \ + \ + void uv_##name##_invoke(uv_loop_t* loop) { \ + uv_##name##_t* handle; \ + \ + (loop)->next_##name##_handle = (loop)->name##_handles; \ + \ + while ((loop)->next_##name##_handle != NULL) { \ + handle = (loop)->next_##name##_handle; \ + (loop)->next_##name##_handle = handle->name##_next; \ + \ + handle->name##_cb(handle, 0); \ + } \ + } + +UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) +UV_LOOP_WATCHER_DEFINE(check, CHECK) +UV_LOOP_WATCHER_DEFINE(idle, IDLE) diff --git a/src/rt/libuv/src/win/pipe.c b/src/rt/libuv/src/win/pipe.c new file mode 100644 index 00000000000..7832c6a41af --- /dev/null +++ b/src/rt/libuv/src/win/pipe.c @@ -0,0 +1,1067 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* A zero-size buffer for use by uv_pipe_read */ +static char uv_zero_[] = ""; + +/* Null uv_buf_t */ +static const uv_buf_t uv_null_buf_ = { 0, NULL }; + +/* The timeout that the pipe will wait for the remote end to write data */ +/* when the local ends wants to shut it down. */ +static const int64_t eof_timeout = 50; /* ms */ + +static void eof_timer_init(uv_pipe_t* pipe); +static void eof_timer_start(uv_pipe_t* pipe); +static void eof_timer_stop(uv_pipe_t* pipe); +static void eof_timer_cb(uv_timer_t* timer, int status); +static void eof_timer_destroy(uv_pipe_t* pipe); +static void eof_timer_close_cb(uv_handle_t* handle); + + +static void uv_unique_pipe_name(char* ptr, char* name, size_t size) { + _snprintf(name, size, "\\\\.\\pipe\\uv\\%p-%d", ptr, GetCurrentProcessId()); +} + + +int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle) { + uv_stream_init(loop, (uv_stream_t*)handle); + + handle->type = UV_NAMED_PIPE; + handle->reqs_pending = 0; + handle->handle = INVALID_HANDLE_VALUE; + handle->name = NULL; + + loop->counters.pipe_init++; + + return 0; +} + + +int uv_pipe_init_with_handle(uv_loop_t* loop, uv_pipe_t* handle, + HANDLE pipeHandle) { + int err = uv_pipe_init(loop, handle); + + if (!err) { + /* + * At this point we don't know whether the pipe will be used as a client + * or a server. So, we assume that it will be a client until + * uv_listen is called. + */ + handle->handle = pipeHandle; + handle->flags |= UV_HANDLE_GIVEN_OS_HANDLE; + } + + return err; +} + + +static void uv_pipe_connection_init(uv_pipe_t* handle) { + uv_connection_init((uv_stream_t*) handle); + handle->eof_timer = NULL; +} + + +int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, + char* name, size_t nameSize) { + HANDLE pipeHandle; + int errno; + int err; + char* ptr = (char*)handle; + + while (TRUE) { + uv_unique_pipe_name(ptr, name, nameSize); + + pipeHandle = CreateNamedPipeA(name, + access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0, + NULL); + + if (pipeHandle != INVALID_HANDLE_VALUE) { + /* No name collisions. We're done. */ + break; + } + + errno = GetLastError(); + if (errno != ERROR_PIPE_BUSY && errno != ERROR_ACCESS_DENIED) { + uv_set_sys_error(loop, errno); + err = -1; + goto done; + } + + /* Pipe name collision. Increment the pointer and try again. */ + ptr++; + } + + if (CreateIoCompletionPort(pipeHandle, + loop->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + + uv_pipe_connection_init(handle); + handle->handle = pipeHandle; + handle->flags |= UV_HANDLE_GIVEN_OS_HANDLE; + err = 0; + +done: + if (err && pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + } + + return err; +} + + +static int uv_set_pipe_handle(uv_loop_t* loop, uv_pipe_t* handle, + HANDLE pipeHandle) { + DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + + if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) { + return -1; + } + + if (CreateIoCompletionPort(pipeHandle, + loop->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + return -1; + } + + return 0; +} + + +static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) { + int errno; + uv_loop_t* loop; + uv_pipe_t* handle; + uv_shutdown_t* req; + + req = (uv_shutdown_t*) parameter; + assert(req); + handle = (uv_pipe_t*) req->handle; + assert(handle); + loop = handle->loop; + assert(loop); + + FlushFileBuffers(handle->handle); + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { + unsigned int uv_alloced; + DWORD result; + uv_shutdown_t* req; + NTSTATUS nt_status; + IO_STATUS_BLOCK io_status; + FILE_PIPE_LOCAL_INFORMATION pipe_info; + + + if (handle->flags & UV_HANDLE_SHUTTING && + !(handle->flags & UV_HANDLE_SHUT) && + handle->write_reqs_pending == 0) { + req = handle->shutdown_req; + + /* Try to avoid flushing the pipe buffer in the thread pool. */ + nt_status = pNtQueryInformationFile(handle->handle, + &io_status, + &pipe_info, + sizeof pipe_info, + FilePipeLocalInformation); + + if (nt_status != STATUS_SUCCESS) { + /* Failure */ + handle->flags &= ~UV_HANDLE_SHUTTING; + if (req->cb) { + uv_set_sys_error(loop, pRtlNtStatusToDosError(nt_status)); + req->cb(req, -1); + } + DECREASE_PENDING_REQ_COUNT(handle); + return; + } + + if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { + handle->flags |= UV_HANDLE_SHUT; + + /* Short-circuit, no need to call FlushFileBuffers. */ + uv_insert_pending_req(loop, (uv_req_t*) req); + return; + } + + /* Run FlushFileBuffers in the thhead pool. */ + result = QueueUserWorkItem(pipe_shutdown_thread_proc, + req, + WT_EXECUTELONGFUNCTION); + if (result) { + /* Mark the handle as shut now to avoid going through this again. */ + handle->flags |= UV_HANDLE_SHUT; + + } else { + /* Failure. */ + handle->flags &= ~UV_HANDLE_SHUTTING; + if (req->cb) { + uv_set_sys_error(loop, GetLastError()); + req->cb(req, -1); + } + DECREASE_PENDING_REQ_COUNT(handle); + return; + } + } + + if (handle->flags & UV_HANDLE_CLOSING && + handle->reqs_pending == 0) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + /* Remember the state of this flag because the close callback is */ + /* allowed to clobber or free the handle's memory */ + uv_alloced = handle->flags & UV_HANDLE_UV_ALLOCED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + if (uv_alloced) { + free(handle); + } + + uv_unref(loop); + } +} + + +/* Creates a pipe server. */ +int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + uv_loop_t* loop = handle->loop; + int i, errno, nameSize; + uv_pipe_accept_t* req; + + if (handle->flags & UV_HANDLE_BOUND) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (!name) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + req = &handle->accept_reqs[i]; + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_ACCEPT; + req->data = handle; + req->pipeHandle = INVALID_HANDLE_VALUE; + req->next_pending = NULL; + } + + /* Convert name to UTF16. */ + nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(wchar_t); + handle->name = (wchar_t*)malloc(nameSize); + if (!handle->name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(wchar_t))) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* + * Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE. + * If this fails then there's already a pipe server for the given pipe name. + */ + handle->accept_reqs[0].pipeHandle = CreateNamedPipeW(handle->name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | + FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); + + if (handle->accept_reqs[0].pipeHandle == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_ACCESS_DENIED) { + uv_set_error(loop, UV_EADDRINUSE, errno); + } else if (errno == ERROR_PATH_NOT_FOUND || errno == ERROR_INVALID_NAME) { + uv_set_error(loop, UV_EACCESS, errno); + } else { + uv_set_sys_error(loop, errno); + } + goto error; + } + + if (uv_set_pipe_handle(loop, handle, handle->accept_reqs[0].pipeHandle)) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + + handle->pending_accepts = NULL; + handle->flags |= UV_HANDLE_PIPESERVER; + handle->flags |= UV_HANDLE_BOUND; + + return 0; + +error: + if (handle->name) { + free(handle->name); + handle->name = NULL; + } + + if (handle->accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->accept_reqs[0].pipeHandle); + handle->accept_reqs[0].pipeHandle = INVALID_HANDLE_VALUE; + } + + return -1; +} + + +static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { + HANDLE pipeHandle = INVALID_HANDLE_VALUE; + int errno; + uv_loop_t* loop; + uv_pipe_t* handle; + uv_connect_t* req; + + req = (uv_connect_t*) parameter; + assert(req); + handle = (uv_pipe_t*) req->handle; + assert(handle); + loop = handle->loop; + assert(loop); + + /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. */ + /* We wait for the pipe to become available with WaitNamedPipe. */ + while (WaitNamedPipeW(handle->name, 30000)) { + /* The pipe is now available, try to connect. */ + pipeHandle = CreateFileW(handle->name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipeHandle != INVALID_HANDLE_VALUE) { + break; + } + + SwitchToThread(); + } + + if (pipeHandle != INVALID_HANDLE_VALUE && + !uv_set_pipe_handle(loop, handle, pipeHandle)) { + handle->handle = pipeHandle; + SET_REQ_SUCCESS(req); + } else { + SET_REQ_ERROR(req, GetLastError()); + } + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +int uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, + const char* name, uv_connect_cb cb) { + uv_loop_t* loop = handle->loop; + int errno, nameSize; + HANDLE pipeHandle; + + handle->handle = INVALID_HANDLE_VALUE; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + + /* Convert name to UTF16. */ + nameSize = uv_utf8_to_utf16(name, NULL, 0) * sizeof(wchar_t); + handle->name = (wchar_t*)malloc(nameSize); + if (!handle->name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(wchar_t))) { + errno = GetLastError(); + goto error; + } + + pipeHandle = CreateFileW(handle->name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipeHandle == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_PIPE_BUSY) { + /* Wait for the server to make a pipe instance available. */ + if (!QueueUserWorkItem(&pipe_connect_thread_proc, + req, + WT_EXECUTELONGFUNCTION)) { + errno = GetLastError(); + goto error; + } + + handle->reqs_pending++; + + return 0; + } + + errno = GetLastError(); + goto error; + } + + if (uv_set_pipe_handle(loop, (uv_pipe_t*)req->handle, pipeHandle)) { + errno = GetLastError(); + goto error; + } + + handle->handle = pipeHandle; + + SET_REQ_SUCCESS(req); + uv_insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + return 0; + +error: + if (handle->name) { + free(handle->name); + handle->name = NULL; + } + + if (pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + } + uv_set_sys_error(loop, errno); + return -1; +} + + +/* Cleans up uv_pipe_t (server or connection) and all resources associated */ +/* with it. */ +void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err) { + int i; + HANDLE pipeHandle; + + if (handle->name) { + free(handle->name); + handle->name = NULL; + } + + if (handle->flags & UV_HANDLE_PIPESERVER) { + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + pipeHandle = handle->accept_reqs[i].pipeHandle; + if (pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + handle->accept_reqs[i].pipeHandle = INVALID_HANDLE_VALUE; + } + } + + } + + if (handle->flags & UV_HANDLE_CONNECTION) { + eof_timer_destroy(handle); + } + + if ((handle->flags & UV_HANDLE_CONNECTION) + && handle->handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->handle); + handle->handle = INVALID_HANDLE_VALUE; + } + + handle->flags |= UV_HANDLE_SHUT; +} + + +static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, + uv_pipe_accept_t* req, BOOL firstInstance) { + assert(handle->flags & UV_HANDLE_LISTENING); + + if (!firstInstance) { + assert(req->pipeHandle == INVALID_HANDLE_VALUE); + + req->pipeHandle = CreateNamedPipeW(handle->name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); + + if (req->pipeHandle == INVALID_HANDLE_VALUE) { + SET_REQ_ERROR(req, GetLastError()); + uv_insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + return; + } + + if (uv_set_pipe_handle(loop, handle, req->pipeHandle)) { + CloseHandle(req->pipeHandle); + req->pipeHandle = INVALID_HANDLE_VALUE; + SET_REQ_ERROR(req, GetLastError()); + uv_insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + return; + } + } + + assert(req->pipeHandle != INVALID_HANDLE_VALUE); + + /* Prepare the overlapped structure. */ + memset(&(req->overlapped), 0, sizeof(req->overlapped)); + + if (!ConnectNamedPipe(req->pipeHandle, &req->overlapped) && + GetLastError() != ERROR_IO_PENDING) { + if (GetLastError() == ERROR_PIPE_CONNECTED) { + SET_REQ_SUCCESS(req); + } else { + CloseHandle(req->pipeHandle); + req->pipeHandle = INVALID_HANDLE_VALUE; + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, GetLastError()); + } + uv_insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + return; + } + + handle->reqs_pending++; +} + + +int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client) { + uv_loop_t* loop = server->loop; + /* Find a connection instance that has been connected, but not yet */ + /* accepted. */ + uv_pipe_accept_t* req = server->pending_accepts; + + if (!req) { + /* No valid connections found, so we error out. */ + uv_set_sys_error(loop, WSAEWOULDBLOCK); + return -1; + } + + /* Initialize the client handle and copy the pipeHandle to the client */ + uv_pipe_connection_init(client); + client->handle = req->pipeHandle; + + /* Prepare the req to pick up a new connection */ + server->pending_accepts = req->next_pending; + req->next_pending = NULL; + req->pipeHandle = INVALID_HANDLE_VALUE; + + if (!(server->flags & UV_HANDLE_CLOSING) && + !(server->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + uv_pipe_queue_accept(loop, server, req, FALSE); + } + + return 0; +} + + +/* Starts listening for connections for the given pipe. */ +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { + uv_loop_t* loop = handle->loop; + + int i, errno; + uv_pipe_accept_t* req; + HANDLE pipeHandle; + + if (!(handle->flags & UV_HANDLE_BOUND) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_LISTENING || + handle->flags & UV_HANDLE_READING) { + uv_set_error(loop, UV_EALREADY, 0); + return -1; + } + + if (!(handle->flags & UV_HANDLE_PIPESERVER) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + uv_set_error(loop, UV_ENOTSUP, 0); + return -1; + } + + handle->flags |= UV_HANDLE_LISTENING; + handle->connection_cb = cb; + + if (handle->flags & UV_HANDLE_GIVEN_OS_HANDLE) { + handle->flags |= UV_HANDLE_PIPESERVER; + pipeHandle = handle->handle; + assert(pipeHandle != INVALID_HANDLE_VALUE); + req = &handle->accept_reqs[0]; + uv_req_init(loop, (uv_req_t*) req); + req->pipeHandle = pipeHandle; + req->type = UV_ACCEPT; + req->data = handle; + req->next_pending = NULL; + + if (uv_set_pipe_handle(loop, handle, pipeHandle)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + uv_pipe_queue_accept(loop, handle, req, TRUE); + } else { + /* First pipe handle should have already been created in uv_pipe_bind */ + assert(handle->accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE); + + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + uv_pipe_queue_accept(loop, handle, &handle->accept_reqs[i], i == 0); + } + } + + return 0; +} + + +static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { + uv_req_t* req; + int result; + + assert(handle->flags & UV_HANDLE_READING); + assert(!(handle->flags & UV_HANDLE_READ_PENDING)); + + assert(handle->handle != INVALID_HANDLE_VALUE); + + req = &handle->read_req; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + /* Do 0-read */ + result = ReadFile(handle->handle, + &uv_zero_, + 0, + NULL, + &req->overlapped); + + if (!result && GetLastError() != ERROR_IO_PENDING) { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv_insert_pending_req(loop, req); + handle->reqs_pending++; + return; + } + + /* Start the eof timer if there is one */ + eof_timer_start(handle); + + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; +} + + +int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + uv_loop_t* loop = handle->loop; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_READING) { + uv_set_error(loop, UV_EALREADY, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_EOF) { + uv_set_error(loop, UV_EOF, 0); + return -1; + } + + handle->flags |= UV_HANDLE_READING; + handle->read_cb = read_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell be a */ + /* read request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + uv_pipe_queue_read(loop, handle); + + return 0; +} + + +int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { + int result; + + if (bufcnt != 1) { + uv_set_error(loop, UV_ENOTSUP, 0); + return -1; + } + + assert(handle->handle != INVALID_HANDLE_VALUE); + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_error(loop, UV_EOF, 0); + return -1; + } + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_WRITE; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + result = WriteFile(handle->handle, + bufs[0].base, + bufs[0].len, + NULL, + &req->overlapped); + + if (!result && GetLastError() != ERROR_IO_PENDING) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (result) { + /* Request completed immediately. */ + req->queued_bytes = 0; + } else { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->write_queue_size += req->queued_bytes; + } + + handle->reqs_pending++; + handle->write_reqs_pending++; + + return 0; +} + + +static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, + uv_buf_t buf) { + /* If there is an eof timer running, we don't need it any more, */ + /* so discard it. */ + eof_timer_destroy(handle); + + handle->flags |= UV_HANDLE_EOF; + uv_read_stop((uv_stream_t*) handle); + + uv_set_error(loop, UV_EOF, 0); + handle->read_cb((uv_stream_t*) handle, -1, uv_null_buf_); +} + + +static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error, + uv_buf_t buf) { + /* If there is an eof timer running, we don't need it any more, */ + /* so discard it. */ + eof_timer_destroy(handle); + + uv_read_stop((uv_stream_t*) handle); + + uv_set_sys_error(loop, error); + handle->read_cb((uv_stream_t*)handle, -1, buf); +} + + +static void uv_pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, + int error, uv_buf_t buf) { + if (error == ERROR_BROKEN_PIPE) { + uv_pipe_read_eof(loop, handle, buf); + } else { + uv_pipe_read_error(loop, handle, error, buf); + } +} + + +void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_req_t* req) { + DWORD bytes, avail; + uv_buf_t buf; + + assert(handle->type == UV_NAMED_PIPE); + + handle->flags &= ~UV_HANDLE_READ_PENDING; + eof_timer_stop(handle); + + if (!REQ_SUCCESS(req)) { + /* An error occurred doing the 0-read. */ + if (handle->flags & UV_HANDLE_READING) { + uv_pipe_read_error_or_eof(loop, + handle, + GET_REQ_ERROR(req), + uv_null_buf_); + } + } else { + /* Do non-blocking reads until the buffer is empty */ + while (handle->flags & UV_HANDLE_READING) { + if (!PeekNamedPipe(handle->handle, + NULL, + 0, + NULL, + &avail, + NULL)) { + uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); + break; + } + + if (avail == 0) { + /* There is nothing to read after all. */ + break; + } + + buf = handle->alloc_cb((uv_handle_t*) handle, avail); + assert(buf.len > 0); + + if (ReadFile(handle->handle, + buf.base, + buf.len, + &bytes, + NULL)) { + /* Successful read */ + handle->read_cb((uv_stream_t*)handle, bytes, buf); + /* Read again only if bytes == buf.len */ + if (bytes <= buf.len) { + break; + } + } else { + uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); + break; + } + } + + /* Post another 0-read if still reading and not closing. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + uv_pipe_queue_read(loop, handle); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_write_t* req) { + assert(handle->type == UV_NAMED_PIPE); + + handle->write_queue_size -= req->queued_bytes; + + if (req->cb) { + if (!REQ_SUCCESS(req)) { + loop->last_error = GET_REQ_UV_ERROR(req); + ((uv_write_cb)req->cb)(req, -1); + } else { + ((uv_write_cb)req->cb)(req, 0); + } + } + + handle->write_reqs_pending--; + if (handle->write_reqs_pending == 0 && + handle->flags & UV_HANDLE_SHUTTING) { + uv_want_endgame(loop, (uv_handle_t*)handle); + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_req_t* raw_req) { + uv_pipe_accept_t* req = (uv_pipe_accept_t*) raw_req; + + assert(handle->type == UV_NAMED_PIPE); + + if (REQ_SUCCESS(req)) { + assert(req->pipeHandle != INVALID_HANDLE_VALUE); + req->next_pending = handle->pending_accepts; + handle->pending_accepts = req; + + if (handle->connection_cb) { + handle->connection_cb((uv_stream_t*)handle, 0); + } + } else { + if (req->pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(req->pipeHandle); + req->pipeHandle = INVALID_HANDLE_VALUE; + } + if (!(handle->flags & UV_HANDLE_CLOSING) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + uv_pipe_queue_accept(loop, handle, req, FALSE); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_connect_t* req) { + assert(handle->type == UV_NAMED_PIPE); + + if (req->cb) { + if (REQ_SUCCESS(req)) { + uv_pipe_connection_init(handle); + ((uv_connect_cb)req->cb)(req, 0); + } else { + loop->last_error = GET_REQ_UV_ERROR(req); + ((uv_connect_cb)req->cb)(req, -1); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_shutdown_t* req) { + assert(handle->type == UV_NAMED_PIPE); + + /* Initialize and optionally start the eof timer. */ + /* This makes no sense if we've already seen EOF. */ + if (!(handle->flags & UV_HANDLE_EOF)) { + eof_timer_init(handle); + + /* If reading start the timer right now. */ + /* Otherwise uv_pipe_queue_read will start it. */ + if (handle->flags & UV_HANDLE_READ_PENDING) { + eof_timer_start(handle); + } + } + + if (req->cb) { + req->cb(req, 0); + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +static void eof_timer_init(uv_pipe_t* pipe) { + int r; + + assert(pipe->eof_timer == NULL); + assert(pipe->flags & UV_HANDLE_CONNECTION); + + pipe->eof_timer = (uv_timer_t*) malloc(sizeof *pipe->eof_timer); + + r = uv_timer_init(pipe->loop, pipe->eof_timer); + assert(r == 0); /* timers can't fail */ + pipe->eof_timer->data = pipe; +} + + +static void eof_timer_start(uv_pipe_t* pipe) { + assert(pipe->flags & UV_HANDLE_CONNECTION); + + if (pipe->eof_timer != NULL) { + uv_timer_start(pipe->eof_timer, eof_timer_cb, eof_timeout, 0); + } +} + + +static void eof_timer_stop(uv_pipe_t* pipe) { + assert(pipe->flags & UV_HANDLE_CONNECTION); + + if (pipe->eof_timer != NULL) { + uv_timer_stop(pipe->eof_timer); + } +} + + +static void eof_timer_cb(uv_timer_t* timer, int status) { + uv_pipe_t* pipe = (uv_pipe_t*) timer->data; + uv_loop_t* loop = timer->loop; + + assert(status == 0); /* timers can't fail */ + assert(pipe->type == UV_NAMED_PIPE); + + /* This should always be true, since we start the timer only */ + /* in uv_pipe_queue_read after successfully calling ReadFile, */ + /* or in uv_process_pipe_shutdown_req if a read is pending, */ + /* and we always immediately stop the timer in */ + /* uv_process_pipe_read_req. */ + assert(pipe->flags & UV_HANDLE_READ_PENDING) ; + + /* If there are many packets coming off the iocp then the timer callback */ + /* may be called before the read request is coming off the queue. */ + /* Therefore we check here if the read request has completed but will */ + /* be processed later. */ + if ((pipe->flags & UV_HANDLE_READ_PENDING) && + HasOverlappedIoCompleted(&pipe->read_req.overlapped)) { + return; + } + + /* Force both ends off the pipe. */ + CloseHandle(pipe->handle); + pipe->handle = INVALID_HANDLE_VALUE; + + /* Stop reading, so the pending read that is going to fail will */ + /* not be reported to the user. */ + uv_read_stop((uv_stream_t*) pipe); + + /* Report the eof and update flags. This will get reported even if the */ + /* user stopped reading in the meantime. TODO: is that okay? */ + uv_pipe_read_eof(loop, pipe, uv_null_buf_); +} + + +static void eof_timer_destroy(uv_pipe_t* pipe) { + assert(pipe->flags && UV_HANDLE_CONNECTION); + + if (pipe->eof_timer) { + uv_close((uv_handle_t*) pipe->eof_timer, eof_timer_close_cb); + pipe->eof_timer = NULL; + } +} + + +static void eof_timer_close_cb(uv_handle_t* handle) { + assert(handle->type == UV_TIMER); + free(handle); +} + + +void uv_pipe_open(uv_pipe_t* pipe, uv_file file) { + assert(0 && "implement me"); +} + diff --git a/src/rt/libuv/src/win/process.c b/src/rt/libuv/src/win/process.c new file mode 100644 index 00000000000..ed8015e0ef6 --- /dev/null +++ b/src/rt/libuv/src/win/process.c @@ -0,0 +1,1045 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + +#include +#include +#include +#include + +typedef struct env_var { + const char* narrow; + const wchar_t* wide; + int len; /* including null or '=' */ + int supplied; + int value_len; +} env_var_t; + +#define E_V(str) { str "=", L##str, sizeof(str), 0, 0 } + +#define UTF8_TO_UTF16(s, t) \ + size = uv_utf8_to_utf16(s, NULL, 0) * sizeof(wchar_t); \ + t = (wchar_t*)malloc(size); \ + if (!t) { \ + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); \ + } \ + if (!uv_utf8_to_utf16(s, t, size / sizeof(wchar_t))) { \ + uv_set_sys_error(loop, GetLastError()); \ + err = -1; \ + goto done; \ + } + + +static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { + handle->type = UV_PROCESS; + handle->loop = loop; + handle->flags = 0; + handle->exit_cb = NULL; + handle->pid = 0; + handle->exit_signal = 0; + handle->wait_handle = INVALID_HANDLE_VALUE; + handle->process_handle = INVALID_HANDLE_VALUE; + handle->close_handle = INVALID_HANDLE_VALUE; + handle->child_stdio[0] = INVALID_HANDLE_VALUE; + handle->child_stdio[1] = INVALID_HANDLE_VALUE; + handle->child_stdio[2] = INVALID_HANDLE_VALUE; + + uv_req_init(loop, (uv_req_t*)&handle->exit_req); + handle->exit_req.type = UV_PROCESS_EXIT; + handle->exit_req.data = handle; + uv_req_init(loop, (uv_req_t*)&handle->close_req); + handle->close_req.type = UV_PROCESS_CLOSE; + handle->close_req.data = handle; + + loop->counters.handle_init++; + loop->counters.process_init++; + + uv_ref(loop); +} + + +/* + * Path search functions + */ + +/* + * Helper function for search_path + */ +static wchar_t* search_path_join_test(const wchar_t* dir, + int dir_len, + const wchar_t* name, + int name_len, + const wchar_t* ext, + int ext_len, + const wchar_t* cwd, + int cwd_len) { + wchar_t *result, *result_pos; + DWORD attrs; + + if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) { + /* It's a full path without drive letter, use cwd's drive letter only */ + cwd_len = 2; + } else if (dir_len >= 2 && dir[1] == L':' && + (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) { + /* It's a relative path with drive letter (ext.g. D:../some/file) + * Replace drive letter in dir by full cwd if it points to the same drive, + * otherwise use the dir only. + */ + if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) { + cwd_len = 0; + } else { + dir += 2; + dir_len -= 2; + } + } else if (dir_len > 2 && dir[1] == L':') { + /* It's an absolute path with drive letter + * Don't use the cwd at all + */ + cwd_len = 0; + } + + /* Allocate buffer for output */ + result = result_pos = (wchar_t*)malloc(sizeof(wchar_t) * + (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1)); + + /* Copy cwd */ + wcsncpy(result_pos, cwd, cwd_len); + result_pos += cwd_len; + + /* Add a path separator if cwd didn't end with one */ + if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { + result_pos[0] = L'\\'; + result_pos++; + } + + /* Copy dir */ + wcsncpy(result_pos, dir, dir_len); + result_pos += dir_len; + + /* Add a separator if the dir didn't end with one */ + if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) { + result_pos[0] = L'\\'; + result_pos++; + } + + /* Copy filename */ + wcsncpy(result_pos, name, name_len); + result_pos += name_len; + + if (ext_len) { + /* Add a dot if the filename didn't end with one */ + if (name_len && result_pos[-1] != '.') { + result_pos[0] = L'.'; + result_pos++; + } + + /* Copy extension */ + wcsncpy(result_pos, ext, ext_len); + result_pos += ext_len; + } + + /* Null terminator */ + result_pos[0] = L'\0'; + + attrs = GetFileAttributesW(result); + + if (attrs != INVALID_FILE_ATTRIBUTES && + !(attrs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) { + return result; + } + + free(result); + return NULL; +} + + +/* + * Helper function for search_path + */ +static wchar_t* path_search_walk_ext(const wchar_t *dir, + int dir_len, + const wchar_t *name, + int name_len, + wchar_t *cwd, + int cwd_len, + int name_has_ext) { + wchar_t* result; + + /* If the name itself has a nonempty extension, try this extension first */ + if (name_has_ext) { + result = search_path_join_test(dir, dir_len, + name, name_len, + L"", 0, + cwd, cwd_len); + if (result != NULL) { + return result; + } + } + + /* Try .com extension */ + result = search_path_join_test(dir, dir_len, + name, name_len, + L"com", 3, + cwd, cwd_len); + if (result != NULL) { + return result; + } + + /* Try .exe extension */ + result = search_path_join_test(dir, dir_len, + name, name_len, + L"exe", 3, + cwd, cwd_len); + if (result != NULL) { + return result; + } + + return NULL; +} + + +/* + * search_path searches the system path for an executable filename - + * the windows API doesn't provide this as a standalone function nor as an + * option to CreateProcess. + * + * It tries to return an absolute filename. + * + * Furthermore, it tries to follow the semantics that cmd.exe, with this + * exception that PATHEXT environment variable isn't used. Since CreateProcess + * can start only .com and .exe files, only those extensions are tried. This + * behavior equals that of msvcrt's spawn functions. + * + * - Do not search the path if the filename already contains a path (either + * relative or absolute). + * + * - If there's really only a filename, check the current directory for file, + * then search all path directories. + * + * - If filename specified has *any* extension, search for the file with the + * specified extension first. + * + * - If the literal filename is not found in a directory, try *appending* + * (not replacing) .com first and then .exe. + * + * - The path variable may contain relative paths; relative paths are relative + * to the cwd. + * + * - Directories in path may or may not end with a trailing backslash. + * + * - CMD does not trim leading/trailing whitespace from path/pathex entries + * nor from the environment variables as a whole. + * + * - When cmd.exe cannot read a directory, it wil just skip it and go on + * searching. However, unlike posix-y systems, it will happily try to run a + * file that is not readable/executable; if the spawn fails it will not + * continue searching. + * + * TODO: correctly interpret UNC paths + */ +static wchar_t* search_path(const wchar_t *file, + wchar_t *cwd, + const wchar_t *path) { + int file_has_dir; + wchar_t* result = NULL; + wchar_t *file_name_start; + wchar_t *dot; + int name_has_ext; + + int file_len = wcslen(file); + int cwd_len = wcslen(cwd); + + /* If the caller supplies an empty filename, + * we're not gonna return c:\windows\.exe -- GFY! + */ + if (file_len == 0 + || (file_len == 1 && file[0] == L'.')) { + return NULL; + } + + /* Find the start of the filename so we can split the directory from the */ + /* name. */ + for (file_name_start = (wchar_t*)file + file_len; + file_name_start > file + && file_name_start[-1] != L'\\' + && file_name_start[-1] != L'/' + && file_name_start[-1] != L':'; + file_name_start--); + + file_has_dir = file_name_start != file; + + /* Check if the filename includes an extension */ + dot = wcschr(file_name_start, L'.'); + name_has_ext = (dot != NULL && dot[1] != L'\0'); + + if (file_has_dir) { + /* The file has a path inside, don't use path */ + result = path_search_walk_ext( + file, file_name_start - file, + file_name_start, file_len - (file_name_start - file), + cwd, cwd_len, + name_has_ext); + + } else { + const wchar_t *dir_start, + *dir_end = path; + + /* The file is really only a name; look in cwd first, then scan path */ + result = path_search_walk_ext(L"", 0, + file, file_len, + cwd, cwd_len, + name_has_ext); + + while (result == NULL) { + if (*dir_end == L'\0') { + break; + } + + /* Skip the separator that dir_end now points to */ + if (dir_end != path) { + dir_end++; + } + + /* Next slice starts just after where the previous one ended */ + dir_start = dir_end; + + /* Slice until the next ; or \0 is found */ + dir_end = wcschr(dir_start, L';'); + if (dir_end == NULL) { + dir_end = wcschr(dir_start, L'\0'); + } + + /* If the slice is zero-length, don't bother */ + if (dir_end - dir_start == 0) { + continue; + } + + result = path_search_walk_ext(dir_start, dir_end - dir_start, + file, file_len, + cwd, cwd_len, + name_has_ext); + } + } + + return result; +} + + +/* + * Quotes command line arguments + * Returns a pointer to the end (next char to be written) of the buffer + */ +wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target) { + int len = wcslen(source), + i, quote_hit; + wchar_t* start; + + /* + * Check if the string must be quoted; + * if unnecessary, don't do it, it may only confuse older programs. + */ + if (len == 0) { + return target; + } + + if (NULL == wcspbrk(source, L" \t\"")) { + /* No quotation needed */ + wcsncpy(target, source, len); + target += len; + return target; + } + + if (NULL == wcspbrk(source, L"\"\\")) { + /* + * No embedded double quotes or backlashes, so I can just wrap + * quote marks around the whole thing. + */ + *(target++) = L'"'; + wcsncpy(target, source, len); + target += len; + *(target++) = L'"'; + return target; + } + + /* + * Expected intput/output: + * input : hello"world + * output: "hello\"world" + * input : hello""world + * output: "hello\"\"world" + * input : hello\world + * output: hello\world + * input : hello\\world + * output: hello\\world + * input : hello\"world + * output: "hello\\\"world" + * input : hello\\"world + * output: "hello\\\\\"world" + * input : hello world\ + * output: "hello world\" + */ + + *(target++) = L'"'; + start = target; + quote_hit = 1; + + for (i = len; i > 0; --i) { + *(target++) = source[i - 1]; + + if (quote_hit && source[i - 1] == L'\\') { + *(target++) = L'\\'; + } else if(source[i - 1] == L'"') { + quote_hit = 1; + *(target++) = L'\\'; + } else { + quote_hit = 0; + } + } + target[0] = L'\0'; + wcsrev(start); + *(target++) = L'"'; + return target; +} + + +wchar_t* make_program_args(char** args, int verbatim_arguments) { + wchar_t* dst; + wchar_t* ptr; + char** arg; + size_t size = 0; + size_t len; + int arg_count = 0; + wchar_t* buffer; + int arg_size; + int buffer_size = 0; + + /* Count the required size. */ + for (arg = args; *arg; arg++) { + arg_size = uv_utf8_to_utf16(*arg, NULL, 0) * sizeof(wchar_t); + size += arg_size; + buffer_size = arg_size > buffer_size ? arg_size : buffer_size; + arg_count++; + } + + /* Adjust for potential quotes. Also assume the worst-case scenario + /* that every character needs escaping, so we need twice as much space. */ + size = size * 2 + arg_count * 2; + + dst = (wchar_t*)malloc(size); + if (!dst) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + buffer = (wchar_t*)malloc(buffer_size); + if (!buffer) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + ptr = dst; + for (arg = args; *arg; arg++) { + len = uv_utf8_to_utf16(*arg, buffer, (size_t)(size - (ptr - dst))); + if (!len) { + goto error; + } + if (verbatim_arguments) { + wcscpy(ptr, buffer); + ptr += len - 1; + } else { + ptr = quote_cmd_arg(buffer, ptr); + } + *ptr++ = *(arg + 1) ? L' ' : L'\0'; + } + + free(buffer); + return dst; + +error: + free(dst); + free(buffer); + return NULL; +} + + +/* + * If we learn that people are passing in huge environment blocks + * then we should probably qsort() the array and then bsearch() + * to see if it contains this variable. But there are ownership + * issues associated with that solution; this is the caller's + * char**, and modifying it is rude. + */ +static void check_required_vars_contains_var(env_var_t* required, int size, + const char* var) { + int i; + for (i = 0; i < size; ++i) { + if (_strnicmp(required[i].narrow, var, required[i].len) == 0) { + required[i].supplied = 1; + return; + } + } +} + + +/* + * The way windows takes environment variables is different than what C does; + * Windows wants a contiguous block of null-terminated strings, terminated + * with an additional null. + * + * Windows has a few "essential" environment variables. winsock will fail + * to initialize if SYSTEMROOT is not defined; some APIs make reference to + * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that + * these get defined if the input environment block does not contain any + * values for them. + */ +wchar_t* make_program_env(char** env_block) { + wchar_t* dst; + wchar_t* ptr; + char** env; + int env_len = 1 * sizeof(wchar_t); /* room for closing null */ + int len; + int i; + DWORD var_size; + + env_var_t required_vars[] = { + E_V("SYSTEMROOT"), + E_V("SYSTEMDRIVE"), + E_V("TEMP"), + }; + + for (env = env_block; *env; env++) { + check_required_vars_contains_var(required_vars, + COUNTOF(required_vars), + *env); + env_len += (uv_utf8_to_utf16(*env, NULL, 0) * sizeof(wchar_t)); + } + + for (i = 0; i < COUNTOF(required_vars); ++i) { + if (!required_vars[i].supplied) { + env_len += required_vars[i].len * sizeof(wchar_t); + var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0); + if (var_size == 0) { + uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); + } + required_vars[i].value_len = (int)var_size; + env_len += (int)var_size * sizeof(wchar_t); + } + } + + dst = malloc(env_len); + if (!dst) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + ptr = dst; + + for (env = env_block; *env; env++, ptr += len) { + len = uv_utf8_to_utf16(*env, ptr, (size_t)(env_len - (ptr - dst))); + if (!len) { + free(dst); + return NULL; + } + } + + for (i = 0; i < COUNTOF(required_vars); ++i) { + if (!required_vars[i].supplied) { + wcscpy(ptr, required_vars[i].wide); + ptr += required_vars[i].len - 1; + *ptr++ = L'='; + var_size = GetEnvironmentVariableW(required_vars[i].wide, + ptr, + required_vars[i].value_len); + if (var_size == 0) { + uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); + } + ptr += required_vars[i].value_len; + } + } + + *ptr = L'\0'; + return dst; +} + + +/* + * Called on Windows thread-pool thread to indicate that + * a child process has exited. + */ +static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { + uv_process_t* process = (uv_process_t*)data; + uv_loop_t* loop = process->loop; + + assert(didTimeout == FALSE); + assert(process); + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, &process->exit_req); +} + + +/* + * Called on Windows thread-pool thread to indicate that + * UnregisterWaitEx has completed. + */ +static void CALLBACK close_wait_callback(void* data, BOOLEAN didTimeout) { + uv_process_t* process = (uv_process_t*)data; + uv_loop_t* loop = process->loop; + + assert(didTimeout == FALSE); + assert(process); + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, &process->close_req); +} + + +/* + * Called on windows thread pool when CreateProcess failed. It writes an error + * message to the process' intended stderr and then posts a PROCESS_EXIT + * packet to the completion port. + */ +static DWORD WINAPI spawn_failure(void* data) { + char syscall[] = "CreateProcessW: "; + char unknown[] = "unknown error\n"; + uv_process_t* process = (uv_process_t*) data; + uv_loop_t* loop = process->loop; + HANDLE child_stderr = process->child_stdio[2]; + char* buf = NULL; + DWORD count, written; + + WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL); + + count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + process->spawn_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &buf, + 0, + NULL); + + if (buf != NULL && count > 0) { + WriteFile(child_stderr, buf, count, &written, NULL); + LocalFree(buf); + } else { + WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL); + } + + FlushFileBuffers(child_stderr); + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, &process->exit_req); + + return 0; +} + + +static void close_child_stdio(uv_process_t* process) { + int i; + HANDLE handle; + + for (i = 0; i < COUNTOF(process->child_stdio); i++) { + handle = process->child_stdio[i]; + if (handle != NULL && handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + process->child_stdio[i] = INVALID_HANDLE_VALUE; + } + } +} + + +/* Called on main thread after a child process has exited. */ +void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { + DWORD exit_code; + + /* Unregister from process notification. */ + if (handle->wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(handle->wait_handle); + handle->wait_handle = INVALID_HANDLE_VALUE; + } + + if (handle->process_handle != INVALID_HANDLE_VALUE) { + /* Get the exit code. */ + if (!GetExitCodeProcess(handle->process_handle, &exit_code)) { + exit_code = 127; + } + + /* Clean-up the process handle. */ + CloseHandle(handle->process_handle); + handle->process_handle = INVALID_HANDLE_VALUE; + } else { + /* We probably left the child stdio handles open to report the error */ + /* asynchronously, so close them now. */ + close_child_stdio(handle); + + /* The process never even started in the first place. */ + exit_code = 127; + } + + /* Fire the exit callback. */ + if (handle->exit_cb) { + handle->exit_cb(handle, exit_code, handle->exit_signal); + } +} + + +/* Called on main thread after UnregisterWaitEx finishes. */ +void uv_process_proc_close(uv_loop_t* loop, uv_process_t* handle) { + uv_want_endgame(loop, (uv_handle_t*)handle); +} + + +void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { + if (handle->wait_handle != INVALID_HANDLE_VALUE) { + handle->close_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + UnregisterWaitEx(handle->wait_handle, handle->close_handle); + handle->wait_handle = NULL; + + RegisterWaitForSingleObject(&handle->wait_handle, handle->close_handle, + close_wait_callback, (void*)handle, INFINITE, + WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); + } else { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} + + +static int uv_create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe, + HANDLE* child_pipe, DWORD server_access, DWORD child_access) { + int err; + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + char pipe_name[64]; + DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + + if (server_pipe->type != UV_NAMED_PIPE) { + uv_set_error(loop, UV_EINVAL, 0); + err = -1; + goto done; + } + + /* Create server pipe handle. */ + err = uv_stdio_pipe_server(loop, + server_pipe, + server_access, + pipe_name, + sizeof(pipe_name)); + if (err) { + goto done; + } + + /* Create child pipe handle. */ + *child_pipe = CreateFileA(pipe_name, + child_access, + 0, + &sa, + OPEN_EXISTING, + 0, + NULL); + + if (*child_pipe == INVALID_HANDLE_VALUE) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + + if (!SetNamedPipeHandleState(*child_pipe, &mode, NULL, NULL)) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + + /* Do a blocking ConnectNamedPipe. This should not block because + * we have both ends of the pipe created. + */ + if (!ConnectNamedPipe(server_pipe->handle, NULL)) { + if (GetLastError() != ERROR_PIPE_CONNECTED) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + } + + err = 0; + +done: + if (err) { + if (server_pipe->handle != INVALID_HANDLE_VALUE) { + close_pipe(server_pipe, NULL, NULL); + } + + if (*child_pipe != INVALID_HANDLE_VALUE) { + CloseHandle(*child_pipe); + *child_pipe = INVALID_HANDLE_VALUE; + } + } + + return err; +} + + +static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) { + HANDLE handle; + HANDLE current_process = GetCurrentProcess(); + + handle = GetStdHandle(id); + + if (handle == NULL) { + *dup = NULL; + return 0; + } else if (handle == INVALID_HANDLE_VALUE) { + *dup = INVALID_HANDLE_VALUE; + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (!DuplicateHandle(current_process, + handle, + current_process, + dup, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) { + *dup = INVALID_HANDLE_VALUE; + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + return 0; +} + + +int uv_spawn(uv_loop_t* loop, uv_process_t* process, + uv_process_options_t options) { + int err = 0, keep_child_stdio_open = 0; + wchar_t* path = NULL; + int size; + BOOL result; + wchar_t* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; + HANDLE* child_stdio = process->child_stdio; + STARTUPINFOW startup; + PROCESS_INFORMATION info; + + if (!options.file) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + uv_process_init(loop, process); + + process->exit_cb = options.exit_cb; + UTF8_TO_UTF16(options.file, application); + arguments = options.args ? make_program_args(options.args, + options.windows_verbatim_arguments) : NULL; + env = options.env ? make_program_env(options.env) : NULL; + + if (options.cwd) { + UTF8_TO_UTF16(options.cwd, cwd); + } else { + size = GetCurrentDirectoryW(0, NULL) * sizeof(wchar_t); + if (size) { + cwd = (wchar_t*)malloc(size); + if (!cwd) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + GetCurrentDirectoryW(size, cwd); + } else { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + } + + /* Get PATH env. variable. */ + size = GetEnvironmentVariableW(L"PATH", NULL, 0) + 1; + path = (wchar_t*)malloc(size * sizeof(wchar_t)); + if (!path) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t)); + path[size - 1] = L'\0'; + + application_path = search_path(application, + cwd, + path); + + if (!application_path) { + /* CreateProcess will fail, but this allows us to pass this error to */ + /* the user asynchronously. */ + application_path = application; + } + + /* Create stdio pipes. */ + if (options.stdin_stream) { + err = uv_create_stdio_pipe_pair( + loop, + options.stdin_stream, + &child_stdio[0], + PIPE_ACCESS_OUTBOUND, + GENERIC_READ | FILE_WRITE_ATTRIBUTES); + } else { + err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]); + } + if (err) { + goto done; + } + + if (options.stdout_stream) { + err = uv_create_stdio_pipe_pair( + loop, options.stdout_stream, + &child_stdio[1], + PIPE_ACCESS_INBOUND, + GENERIC_WRITE); + } else { + err = duplicate_std_handle(loop, STD_OUTPUT_HANDLE, &child_stdio[1]); + } + if (err) { + goto done; + } + + if (options.stderr_stream) { + err = uv_create_stdio_pipe_pair( + loop, + options.stderr_stream, + &child_stdio[2], + PIPE_ACCESS_INBOUND, + GENERIC_WRITE); + } else { + err = duplicate_std_handle(loop, STD_ERROR_HANDLE, &child_stdio[2]); + } + if (err) { + goto done; + } + + startup.cb = sizeof(startup); + startup.lpReserved = NULL; + startup.lpDesktop = NULL; + startup.lpTitle = NULL; + startup.dwFlags = STARTF_USESTDHANDLES; + startup.cbReserved2 = 0; + startup.lpReserved2 = NULL; + startup.hStdInput = child_stdio[0]; + startup.hStdOutput = child_stdio[1]; + startup.hStdError = child_stdio[2]; + + if (CreateProcessW(application_path, + arguments, + NULL, + NULL, + 1, + CREATE_UNICODE_ENVIRONMENT, + env, + cwd, + &startup, + &info)) { + /* Spawn succeeded */ + process->process_handle = info.hProcess; + process->pid = info.dwProcessId; + + /* Setup notifications for when the child process exits. */ + result = RegisterWaitForSingleObject(&process->wait_handle, + process->process_handle, exit_wait_callback, (void*)process, INFINITE, + WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); + if (!result) { + uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); + } + + CloseHandle(info.hThread); + + } else { + /* CreateProcessW failed, but this failure should be delivered */ + /* asynchronously to retain unix compatibility. So pretent spawn */ + /* succeeded, and start a thread instead that prints an error */ + /* to the child's intended stderr. */ + process->spawn_errno = GetLastError(); + keep_child_stdio_open = 1; + if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) { + uv_fatal_error(GetLastError(), "QueueUserWorkItem"); + } + } + +done: + free(application); + if (application_path != application) { + free(application_path); + } + free(arguments); + free(cwd); + free(env); + free(path); + + /* Under normal circumstances we should close the stdio handles now - */ + /* the child now has its own duplicates, or something went horribly wrong. */ + /* The only exception is when CreateProcess has failed, then we actually */ + /* need to keep the stdio handles to report the error asynchronously. */ + if (!keep_child_stdio_open) { + close_child_stdio(process); + } else { + /* We're keeping the handles open, the thread pool is going to have */ + /* it's way with them. But at least make them noninheritable. */ + int i; + for (i = 0; i < COUNTOF(process->child_stdio); i++) { + SetHandleInformation(child_stdio[i], HANDLE_FLAG_INHERIT, 0); + } + } + + if (err) { + if (process->wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(process->wait_handle); + process->wait_handle = INVALID_HANDLE_VALUE; + } + + if (process->process_handle != INVALID_HANDLE_VALUE) { + CloseHandle(process->process_handle); + process->process_handle = INVALID_HANDLE_VALUE; + } + } + + return err; +} + + +int uv_process_kill(uv_process_t* process, int signum) { + process->exit_signal = signum; + + /* On windows killed processes normally return 1 */ + if (process->process_handle != INVALID_HANDLE_VALUE && + TerminateProcess(process->process_handle, 1)) { + return 0; + } + + return -1; +} diff --git a/src/rt/libuv/src/win/req.c b/src/rt/libuv/src/win/req.c new file mode 100644 index 00000000000..a0a6e03dc60 --- /dev/null +++ b/src/rt/libuv/src/win/req.c @@ -0,0 +1,176 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_req_init(uv_loop_t* loop, uv_req_t* req) { + loop->counters.req_init++; + req->type = UV_UNKNOWN_REQ; + SET_REQ_SUCCESS(req); +} + + +uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { + return CONTAINING_RECORD(overlapped, uv_req_t, overlapped); +} + + +void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) { + req->next_req = NULL; + if (loop->pending_reqs_tail) { + req->next_req = loop->pending_reqs_tail->next_req; + loop->pending_reqs_tail->next_req = req; + loop->pending_reqs_tail = req; + } else { + req->next_req = req; + loop->pending_reqs_tail = req; + } +} + + +static uv_req_t* uv_remove_pending_req(uv_loop_t* loop) { + uv_req_t* req; + + if (loop->pending_reqs_tail) { + req = loop->pending_reqs_tail->next_req; + + if (req == loop->pending_reqs_tail) { + loop->pending_reqs_tail = NULL; + } else { + loop->pending_reqs_tail->next_req = req->next_req; + } + + return req; + + } else { + /* queue empty */ + return NULL; + } +} + + +#define DELEGATE_STREAM_REQ(loop, req, method, handle_at) \ + do { \ + switch (((uv_handle_t*) (req)->handle_at)->type) { \ + case UV_TCP: \ + uv_process_tcp_##method##_req(loop, \ + (uv_tcp_t*) ((req)->handle_at), \ + req); \ + break; \ + \ + case UV_NAMED_PIPE: \ + uv_process_pipe_##method##_req(loop, \ + (uv_pipe_t*) ((req)->handle_at), \ + req); \ + break; \ + \ + default: \ + assert(0); \ + } \ + } while (0) + + +void uv_process_reqs(uv_loop_t* loop) { + uv_req_t* req; + + while (req = uv_remove_pending_req(loop)) { + switch (req->type) { + case UV_READ: + DELEGATE_STREAM_REQ(loop, req, read, data); + break; + + case UV_WRITE: + DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle); + break; + + case UV_ACCEPT: + DELEGATE_STREAM_REQ(loop, req, accept, data); + break; + + case UV_CONNECT: + DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle); + break; + + case UV_SHUTDOWN: + /* Tcp shutdown requests don't come here. */ + assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE); + uv_process_pipe_shutdown_req( + loop, + (uv_pipe_t*) ((uv_shutdown_t*) req)->handle, + (uv_shutdown_t*) req); + break; + + case UV_UDP_RECV: + uv_process_udp_recv_req(loop, (uv_udp_t*) req->data, req); + break; + + case UV_UDP_SEND: + uv_process_udp_send_req(loop, + ((uv_udp_send_t*) req)->handle, + (uv_udp_send_t*) req); + break; + + case UV_WAKEUP: + uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req); + break; + + case UV_ARES_EVENT_REQ: + uv_process_ares_event_req(loop, (uv_ares_action_t*) req->data, req); + break; + + case UV_ARES_CLEANUP_REQ: + uv_process_ares_cleanup_req(loop, (uv_ares_task_t*) req->data, req); + break; + + case UV_GETADDRINFO_REQ: + uv_process_getaddrinfo_req(loop, (uv_getaddrinfo_t*) req->data, req); + break; + + case UV_PROCESS_EXIT: + uv_process_proc_exit(loop, (uv_process_t*) req->data); + break; + + case UV_PROCESS_CLOSE: + uv_process_proc_close(loop, (uv_process_t*) req->data); + break; + + case UV_FS: + uv_process_fs_req(loop, (uv_fs_t*) req); + break; + + case UV_WORK: + uv_process_work_req(loop, (uv_work_t*) req); + break; + + case UV_FS_EVENT_REQ: + uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); + break; + + default: + assert(0); + } + } +} diff --git a/src/rt/libuv/src/win/stdio.c b/src/rt/libuv/src/win/stdio.c new file mode 100644 index 00000000000..30fe2a1d6e1 --- /dev/null +++ b/src/rt/libuv/src/win/stdio.c @@ -0,0 +1,75 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +static uv_pipe_t* uv_make_pipe_for_std_handle(uv_loop_t* loop, HANDLE handle) { + uv_pipe_t* pipe = NULL; + + pipe = (uv_pipe_t*)malloc(sizeof(uv_pipe_t)); + if (!pipe) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (uv_pipe_init_with_handle(loop, pipe, handle)) { + free(pipe); + return NULL; + } + + pipe->flags |= UV_HANDLE_UV_ALLOCED; + return pipe; +} + + +uv_stream_t* uv_std_handle(uv_loop_t* loop, uv_std_type type) { + HANDLE handle; + + switch (type) { + case UV_STDIN: + handle = GetStdHandle(STD_INPUT_HANDLE); + if (handle == INVALID_HANDLE_VALUE) { + return NULL; + } + + /* Assume only named pipes for now. */ + return (uv_stream_t*)uv_make_pipe_for_std_handle(loop, handle); + break; + + case UV_STDOUT: + return NULL; + break; + + case UV_STDERR: + return NULL; + break; + + default: + assert(0); + uv_set_error(loop, UV_EINVAL, 0); + return NULL; + } +} diff --git a/src/rt/libuv/src/win/stream.c b/src/rt/libuv/src/win/stream.c new file mode 100644 index 00000000000..26964110fce --- /dev/null +++ b/src/rt/libuv/src/win/stream.c @@ -0,0 +1,154 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_stream_init(uv_loop_t* loop, uv_stream_t* handle) { + handle->write_queue_size = 0; + handle->loop = loop; + handle->flags = 0; + + loop->counters.handle_init++; + loop->counters.stream_init++; + + uv_ref(loop); +} + + +void uv_connection_init(uv_stream_t* handle) { + handle->flags |= UV_HANDLE_CONNECTION; + handle->write_reqs_pending = 0; + + uv_req_init(handle->loop, (uv_req_t*) &(handle->read_req)); + handle->read_req.type = UV_READ; + handle->read_req.data = handle; +} + + +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { + switch (stream->type) { + case UV_TCP: + return uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + case UV_NAMED_PIPE: + return uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + default: + assert(0); + return -1; + } +} + + +int uv_accept(uv_stream_t* server, uv_stream_t* client) { + assert(client->type == server->type); + + switch (server->type) { + case UV_TCP: + return uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); + case UV_NAMED_PIPE: + return uv_pipe_accept((uv_pipe_t*)server, (uv_pipe_t*)client); + default: + assert(0); + return -1; + } +} + + +int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + switch (handle->type) { + case UV_TCP: + return uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); + case UV_NAMED_PIPE: + return uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); + default: + assert(0); + return -1; + } +} + + +int uv_read_stop(uv_stream_t* handle) { + handle->flags &= ~UV_HANDLE_READING; + + return 0; +} + + +int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb) { + uv_loop_t* loop = handle->loop; + + switch (handle->type) { + case UV_TCP: + return uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, bufcnt, cb); + case UV_NAMED_PIPE: + return uv_pipe_write(loop, req, (uv_pipe_t*) handle, bufs, bufcnt, cb); + default: + assert(0); + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } +} + + +int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { + uv_loop_t* loop = handle->loop; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(loop, WSAESHUTDOWN); + return -1; + } + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_SHUTDOWN; + req->handle = handle; + req->cb = cb; + + handle->flags |= UV_HANDLE_SHUTTING; + handle->shutdown_req = req; + handle->reqs_pending++; + + uv_want_endgame(loop, (uv_handle_t*)handle); + + return 0; +} + + +size_t uv_count_bufs(uv_buf_t bufs[], int count) { + size_t bytes = 0; + int i; + + for (i = 0; i < count; i++) { + bytes += (size_t)bufs[i].len; + } + + return bytes; +} diff --git a/src/rt/libuv/src/win/tcp.c b/src/rt/libuv/src/win/tcp.c new file mode 100644 index 00000000000..ebd83534983 --- /dev/null +++ b/src/rt/libuv/src/win/tcp.c @@ -0,0 +1,870 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* + * Threshold of active tcp streams for which to preallocate tcp read buffers. + * (Due to node slab allocator performing poorly under this pattern, + * the optimization is temporarily disabled (threshold=0). This will be + * revisited once node allocator is improved.) + */ +const unsigned int uv_active_tcp_streams_threshold = 0; + +/* + * Number of simultaneous pending AcceptEx calls. + */ +const unsigned int uv_simultaneous_server_accepts = 32; + +/* A zero-size buffer for use by uv_tcp_read */ +static char uv_zero_[] = ""; + +/* Counter to keep track of active tcp streams */ +static unsigned int active_tcp_streams = 0; + + +static int uv_tcp_set_socket(uv_loop_t* loop, uv_tcp_t* handle, + SOCKET socket) { + DWORD yes = 1; + + assert(handle->socket == INVALID_SOCKET); + + /* Set the socket to nonblocking mode */ + if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + /* Make the socket non-inheritable */ + if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* Associate it with the I/O completion port. */ + /* Use uv_handle_t pointer as completion key. */ + if (CreateIoCompletionPort((HANDLE)socket, + loop->iocp, + (ULONG_PTR)socket, + 0) == NULL) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (pSetFileCompletionNotificationModes) { + if (pSetFileCompletionNotificationModes((HANDLE) socket, + FILE_SKIP_SET_EVENT_ON_HANDLE | + FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { + handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; + } else if (GetLastError() != ERROR_INVALID_FUNCTION) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + } + + handle->socket = socket; + + return 0; +} + + +int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) { + uv_stream_init(loop, (uv_stream_t*)handle); + + handle->accept_reqs = NULL; + handle->pending_accepts = NULL; + handle->socket = INVALID_SOCKET; + handle->type = UV_TCP; + handle->reqs_pending = 0; + + loop->counters.tcp_init++; + + return 0; +} + + +void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { + uv_err_t err; + int status; + + if (handle->flags & UV_HANDLE_CONNECTION && + handle->flags & UV_HANDLE_SHUTTING && + !(handle->flags & UV_HANDLE_SHUT) && + handle->write_reqs_pending == 0) { + + if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { + status = 0; + handle->flags |= UV_HANDLE_SHUT; + } else { + status = -1; + err = uv_new_sys_error(WSAGetLastError()); + } + if (handle->shutdown_req->cb) { + if (status == -1) { + loop->last_error = err; + } + handle->shutdown_req->cb(handle->shutdown_req, status); + } + handle->reqs_pending--; + } + + if (handle->flags & UV_HANDLE_CLOSING && + handle->reqs_pending == 0) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->accept_reqs) { + free(handle->accept_reqs); + handle->accept_reqs = NULL; + } + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + active_tcp_streams--; + + uv_unref(loop); + } +} + + +static int uv__bind(uv_loop_t* loop, uv_tcp_t* handle, int domain, + struct sockaddr* addr, int addrsize) { + DWORD err; + int r; + SOCKET sock; + + if (handle->socket == INVALID_SOCKET) { + sock = socket(domain, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + if (uv_tcp_set_socket(loop, handle, sock) == -1) { + closesocket(sock); + return -1; + } + } + + r = bind(handle->socket, addr, addrsize); + + if (r == SOCKET_ERROR) { + err = WSAGetLastError(); + if (err == WSAEADDRINUSE) { + /* Some errors are not to be reported until connect() or listen() */ + handle->bind_error = uv_new_sys_error(err); + handle->flags |= UV_HANDLE_BIND_ERROR; + } else { + uv_set_sys_error(loop, err); + return -1; + } + } + + handle->flags |= UV_HANDLE_BOUND; + + return 0; +} + + +int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { + uv_loop_t* loop = handle->loop; + + if (addr.sin_family != AF_INET) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + return uv__bind(loop, + handle, + AF_INET, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)); +} + + +int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { + uv_loop_t* loop = handle->loop; + + if (addr.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (uv_allow_ipv6) { + handle->flags |= UV_HANDLE_IPV6; + return uv__bind(loop, + handle, + AF_INET6, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in6)); + + } else { + uv_set_sys_error(loop, WSAEAFNOSUPPORT); + return -1; + } +} + + +static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { + uv_loop_t* loop = handle->loop; + BOOL success; + DWORD bytes; + SOCKET accept_socket; + short family; + LPFN_ACCEPTEX pAcceptExFamily; + + assert(handle->flags & UV_HANDLE_LISTENING); + assert(req->accept_socket == INVALID_SOCKET); + + /* choose family and extension function */ + if ((handle->flags & UV_HANDLE_IPV6) != 0) { + family = AF_INET6; + pAcceptExFamily = pAcceptEx6; + } else { + family = AF_INET; + pAcceptExFamily = pAcceptEx; + } + + /* Open a socket for the accepted connection. */ + accept_socket = socket(family, SOCK_STREAM, 0); + if (accept_socket == INVALID_SOCKET) { + SET_REQ_ERROR(req, WSAGetLastError()); + uv_insert_pending_req(loop, (uv_req_t*)req); + handle->reqs_pending++; + return; + } + + /* Prepare the overlapped structure. */ + memset(&(req->overlapped), 0, sizeof(req->overlapped)); + + success = pAcceptExFamily(handle->socket, + accept_socket, + (void*)req->accept_buffer, + 0, + sizeof(struct sockaddr_storage), + sizeof(struct sockaddr_storage), + &bytes, + &req->overlapped); + + if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { + /* Process the req without IOCP. */ + req->accept_socket = accept_socket; + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(success)) { + /* The req will be processed with IOCP. */ + req->accept_socket = accept_socket; + handle->reqs_pending++; + } else { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv_insert_pending_req(loop, (uv_req_t*)req); + handle->reqs_pending++; + /* Destroy the preallocated client socket. */ + closesocket(accept_socket); + } +} + + +static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { + uv_req_t* req; + uv_buf_t buf; + int result; + DWORD bytes, flags; + + assert(handle->flags & UV_HANDLE_READING); + assert(!(handle->flags & UV_HANDLE_READ_PENDING)); + + req = &handle->read_req; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + /* + * Preallocate a read buffer if the number of active streams is below + * the threshold. + */ + if (active_tcp_streams < uv_active_tcp_streams_threshold) { + handle->flags &= ~UV_HANDLE_ZERO_READ; + handle->read_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(handle->read_buffer.len > 0); + buf = handle->read_buffer; + } else { + handle->flags |= UV_HANDLE_ZERO_READ; + buf.base = (char*) &uv_zero_; + buf.len = 0; + } + + flags = 0; + result = WSARecv(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->overlapped.InternalHigh = bytes; + handle->reqs_pending++; + uv_insert_pending_req(loop, req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* The req will be processed with IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; + } else { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv_insert_pending_req(loop, req); + handle->reqs_pending++; + } +} + + +int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { + uv_loop_t* loop = handle->loop; + unsigned int i; + uv_tcp_accept_t* req; + + assert(backlog > 0); + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + if (handle->flags & UV_HANDLE_LISTENING || + handle->flags & UV_HANDLE_READING) { + /* Already listening. */ + uv_set_sys_error(loop, WSAEALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) + return -1; + + if (listen(handle->socket, backlog) == SOCKET_ERROR) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + handle->flags |= UV_HANDLE_LISTENING; + handle->connection_cb = cb; + + assert(!handle->accept_reqs); + handle->accept_reqs = (uv_tcp_accept_t*) + malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t)); + if (!handle->accept_reqs) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + for (i = 0; i < uv_simultaneous_server_accepts; i++) { + req = &handle->accept_reqs[i]; + uv_req_init(loop, (uv_req_t*)req); + req->type = UV_ACCEPT; + req->accept_socket = INVALID_SOCKET; + req->data = handle; + uv_tcp_queue_accept(handle, req); + } + + return 0; +} + + +int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { + uv_loop_t* loop = server->loop; + int rv = 0; + + uv_tcp_accept_t* req = server->pending_accepts; + + if (!req) { + /* No valid connections found, so we error out. */ + uv_set_sys_error(loop, WSAEWOULDBLOCK); + return -1; + } + + if (req->accept_socket == INVALID_SOCKET) { + uv_set_sys_error(loop, WSAENOTCONN); + return -1; + } + + if (uv_tcp_set_socket(client->loop, client, req->accept_socket) == -1) { + closesocket(req->accept_socket); + rv = -1; + } else { + uv_connection_init((uv_stream_t*) client); + /* AcceptEx() implicitly binds the accepted socket. */ + client->flags |= UV_HANDLE_BOUND; + } + + /* Prepare the req to pick up a new connection */ + server->pending_accepts = req->next_pending; + req->next_pending = NULL; + req->accept_socket = INVALID_SOCKET; + + if (!(server->flags & UV_HANDLE_CLOSING)) { + uv_tcp_queue_accept(server, req); + } + + active_tcp_streams++; + + return rv; +} + + +int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + uv_loop_t* loop = handle->loop; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_READING) { + uv_set_sys_error(loop, WSAEALREADY); + return -1; + } + + if (handle->flags & UV_HANDLE_EOF) { + uv_set_sys_error(loop, WSAESHUTDOWN); + return -1; + } + + handle->flags |= UV_HANDLE_READING; + handle->read_cb = read_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell be a */ + /* read request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + uv_tcp_queue_read(loop, handle); + + return 0; +} + + +int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in address, uv_connect_cb cb) { + uv_loop_t* loop = handle->loop; + int addrsize = sizeof(struct sockaddr_in); + BOOL success; + DWORD bytes; + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + if (address.sin_family != AF_INET) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) + return -1; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { + /* Process the req without IOCP. */ + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(success)) { + /* The req will be processed with IOCP. */ + handle->reqs_pending++; + } else { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_connect6(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in6 address, uv_connect_cb cb) { + uv_loop_t* loop = handle->loop; + int addrsize = sizeof(struct sockaddr_in6); + BOOL success; + DWORD bytes; + + if (!uv_allow_ipv6) { + uv_new_sys_error(WSAEAFNOSUPPORT); + return -1; + } + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + if (address.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind6(handle, uv_addr_ip6_any_) < 0) + return -1; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx6(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(success)) { + handle->reqs_pending++; + } else { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + uv_loop_t* loop = handle->loop; + int result; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + result = getsockname(handle->socket, name, namelen); + if (result != 0) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + uv_loop_t* loop = handle->loop; + int result; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + result = getpeername(handle->socket, name, namelen); + if (result != 0) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { + int result; + DWORD bytes; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(loop, WSAESHUTDOWN); + return -1; + } + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_WRITE; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + result = WSASend(handle->socket, + (WSABUF*)bufs, + bufcnt, + &bytes, + 0, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Request completed immediately. */ + req->queued_bytes = 0; + handle->reqs_pending++; + handle->write_reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*) req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->reqs_pending++; + handle->write_reqs_pending++; + handle->write_queue_size += req->queued_bytes; + } else { + /* Send failed due to an error. */ + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, + uv_req_t* req) { + DWORD bytes, flags, err; + uv_buf_t buf; + + assert(handle->type == UV_TCP); + + handle->flags &= ~UV_HANDLE_READ_PENDING; + + if (!REQ_SUCCESS(req)) { + /* An error occurred doing the read. */ + if ((handle->flags & UV_HANDLE_READING)) { + handle->flags &= ~UV_HANDLE_READING; + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + buf = (handle->flags & UV_HANDLE_ZERO_READ) ? + uv_buf_init(NULL, 0) : handle->read_buffer; + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + } else { + if (!(handle->flags & UV_HANDLE_ZERO_READ)) { + /* The read was done with a non-zero buffer length. */ + if (req->overlapped.InternalHigh > 0) { + /* Successful read */ + handle->read_cb((uv_stream_t*)handle, + req->overlapped.InternalHigh, + handle->read_buffer); + /* Read again only if bytes == buf.len */ + if (req->overlapped.InternalHigh < handle->read_buffer.len) { + goto done; + } + } else { + /* Connection closed */ + handle->flags &= ~UV_HANDLE_READING; + handle->flags |= UV_HANDLE_EOF; + loop->last_error.code = UV_EOF; + loop->last_error.sys_errno_ = ERROR_SUCCESS; + buf.base = 0; + buf.len = 0; + handle->read_cb((uv_stream_t*)handle, -1, handle->read_buffer); + goto done; + } + } + + /* Do nonblocking reads until the buffer is empty */ + while (handle->flags & UV_HANDLE_READING) { + buf = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(buf.len > 0); + flags = 0; + if (WSARecv(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + NULL, + NULL) != SOCKET_ERROR) { + if (bytes > 0) { + /* Successful read */ + handle->read_cb((uv_stream_t*)handle, bytes, buf); + /* Read again only if bytes == buf.len */ + if (bytes < buf.len) { + break; + } + } else { + /* Connection closed */ + handle->flags &= ~UV_HANDLE_READING; + handle->flags |= UV_HANDLE_EOF; + loop->last_error.code = UV_EOF; + loop->last_error.sys_errno_ = ERROR_SUCCESS; + handle->read_cb((uv_stream_t*)handle, -1, buf); + break; + } + } else { + err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { + /* Read buffer was completely empty, report a 0-byte read. */ + uv_set_sys_error(loop, WSAEWOULDBLOCK); + handle->read_cb((uv_stream_t*)handle, 0, buf); + } else { + /* Ouch! serious error. */ + uv_set_sys_error(loop, err); + handle->flags &= ~UV_HANDLE_READING; + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + break; + } + } + +done: + /* Post another read if still reading and not closing. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + uv_tcp_queue_read(loop, handle); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, + uv_write_t* req) { + assert(handle->type == UV_TCP); + + handle->write_queue_size -= req->queued_bytes; + + if (req->cb) { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + ((uv_write_cb)req->cb)(req, loop->last_error.code == UV_OK ? 0 : -1); + } + + handle->write_reqs_pending--; + if (handle->flags & UV_HANDLE_SHUTTING && + handle->write_reqs_pending == 0) { + uv_want_endgame(loop, (uv_handle_t*)handle); + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, + uv_req_t* raw_req) { + uv_tcp_accept_t* req = (uv_tcp_accept_t*) raw_req; + + assert(handle->type == UV_TCP); + + /* If handle->accepted_socket is not a valid socket, then */ + /* uv_queue_accept must have failed. This is a serious error. We stop */ + /* accepting connections and report this error to the connection */ + /* callback. */ + if (req->accept_socket == INVALID_SOCKET) { + if (handle->flags & UV_HANDLE_LISTENING) { + handle->flags &= ~UV_HANDLE_LISTENING; + if (handle->connection_cb) { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + handle->connection_cb((uv_stream_t*)handle, -1); + } + } + } else if (REQ_SUCCESS(req) && + setsockopt(req->accept_socket, + SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char*)&handle->socket, + sizeof(handle->socket)) == 0) { + req->next_pending = handle->pending_accepts; + handle->pending_accepts = req; + + /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */ + if (handle->connection_cb) { + handle->connection_cb((uv_stream_t*)handle, 0); + } + } else { + /* Error related to accepted socket is ignored because the server */ + /* socket may still be healthy. If the server socket is broken + /* uv_queue_accept will detect it. */ + closesocket(req->accept_socket); + req->accept_socket = INVALID_SOCKET; + if (handle->flags & UV_HANDLE_LISTENING) { + uv_tcp_queue_accept(handle, req); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, + uv_connect_t* req) { + assert(handle->type == UV_TCP); + + if (req->cb) { + if (REQ_SUCCESS(req)) { + if (setsockopt(handle->socket, + SOL_SOCKET, + SO_UPDATE_CONNECT_CONTEXT, + NULL, + 0) == 0) { + uv_connection_init((uv_stream_t*)handle); + active_tcp_streams++; + ((uv_connect_cb)req->cb)(req, 0); + } else { + uv_set_sys_error(loop, WSAGetLastError()); + ((uv_connect_cb)req->cb)(req, -1); + } + } else { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + ((uv_connect_cb)req->cb)(req, -1); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} diff --git a/src/rt/libuv/src/win/threadpool.c b/src/rt/libuv/src/win/threadpool.c new file mode 100644 index 00000000000..c40613e1f5c --- /dev/null +++ b/src/rt/libuv/src/win/threadpool.c @@ -0,0 +1,73 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "internal.h" + + +static void uv_work_req_init(uv_loop_t* loop, uv_work_t* req, + uv_work_cb work_cb, uv_after_work_cb after_work_cb) { + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_WORK; + req->loop = loop; + req->work_cb = work_cb; + req->after_work_cb = after_work_cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); +} + + +static DWORD WINAPI uv_work_thread_proc(void* parameter) { + uv_work_t* req = (uv_work_t*)parameter; + uv_loop_t* loop = req->loop; + + assert(req != NULL); + assert(req->type == UV_WORK); + assert(req->work_cb); + + req->work_cb(req); + + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, + uv_after_work_cb after_work_cb) { + uv_work_req_init(loop, req, work_cb, after_work_cb); + + if (!QueueUserWorkItem(&uv_work_thread_proc, req, WT_EXECUTELONGFUNCTION)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + uv_ref(loop); + return 0; +} + + +void uv_process_work_req(uv_loop_t* loop, uv_work_t* req) { + assert(req->after_work_cb); + req->after_work_cb(req); + uv_unref(loop); +} diff --git a/src/rt/libuv/src/win/threads.c b/src/rt/libuv/src/win/threads.c new file mode 100644 index 00000000000..1fc6b73f870 --- /dev/null +++ b/src/rt/libuv/src/win/threads.c @@ -0,0 +1,81 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +#ifdef _MSC_VER /* msvc */ +# define NOINLINE __declspec (noinline) +#else /* gcc */ +# define NOINLINE __attribute__ ((noinline)) +#endif + + +static NOINLINE void uv__once_inner(uv_once_t* guard, + void (*callback)(void)) { + DWORD result; + HANDLE existing_event, created_event; + HANDLE* event_ptr; + + /* Fetch and align event_ptr */ + event_ptr = (HANDLE*) (((uintptr_t) &guard->event + (sizeof(HANDLE) - 1)) & + ~(sizeof(HANDLE) - 1)); + + created_event = CreateEvent(NULL, 1, 0, NULL); + if (created_event == 0) { + /* Could fail in a low-memory situation? */ + uv_fatal_error(GetLastError(), "CreateEvent"); + } + + existing_event = InterlockedCompareExchangePointer(event_ptr, + created_event, + NULL); + + if (existing_event == NULL) { + /* We won the race */ + callback(); + + result = SetEvent(created_event); + assert(result); + guard->ran = 1; + + } else { + /* We lost the race. Destroy the event we created and wait for the */ + /* existing one to become signaled. */ + CloseHandle(created_event); + result = WaitForSingleObject(existing_event, INFINITE); + assert(result == WAIT_OBJECT_0); + } +} + + +void uv_once(uv_once_t* guard, void (*callback)(void)) { + /* Fast case - avoid WaitForSingleObject. */ + if (guard->ran) { + return; + } + + uv__once_inner(guard, callback); +} diff --git a/src/rt/libuv/src/win/timer.c b/src/rt/libuv/src/win/timer.c new file mode 100644 index 00000000000..ac20925293f --- /dev/null +++ b/src/rt/libuv/src/win/timer.c @@ -0,0 +1,276 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include "uv.h" +#include "internal.h" +#include "tree.h" + +#undef NANOSEC +#define NANOSEC 1000000000 + + +/* The resolution of the high-resolution clock. */ +static int64_t uv_ticks_per_msec_ = 0; +static uint64_t uv_hrtime_frequency_ = 0; +static uv_once_t uv_hrtime_init_guard_ = UV_ONCE_INIT; + + +void uv_update_time(uv_loop_t* loop) { + DWORD ticks = GetTickCount(); + + /* The assumption is made that LARGE_INTEGER.QuadPart has the same type */ + /* loop->time, which happens to be. Is there any way to assert this? */ + LARGE_INTEGER* time = (LARGE_INTEGER*) &loop->time; + + /* If the timer has wrapped, add 1 to it's high-order dword. */ + /* uv_poll must make sure that the timer can never overflow more than */ + /* once between two subsequent uv_update_time calls. */ + if (ticks < time->LowPart) { + time->HighPart += 1; + } + time->LowPart = ticks; +} + + +int64_t uv_now(uv_loop_t* loop) { + return loop->time; +} + + +static void uv_hrtime_init(void) { + LARGE_INTEGER frequency; + + if (!QueryPerformanceFrequency(&frequency)) { + uv_hrtime_frequency_ = 0; + return; + } + + uv_hrtime_frequency_ = frequency.QuadPart; +} + + +uint64_t uv_hrtime(void) { + LARGE_INTEGER counter; + + uv_once(&uv_hrtime_init_guard_, uv_hrtime_init); + + /* If the performance frequency is zero, there's no support. */ + if (!uv_hrtime_frequency_) { + /* uv_set_sys_error(loop, ERROR_NOT_SUPPORTED); */ + return 0; + } + + if (!QueryPerformanceCounter(&counter)) { + /* uv_set_sys_error(loop, GetLastError()); */ + return 0; + } + + /* Because we have no guarantee about the order of magniture of the */ + /* performance counter frequency, and there may not be much headroom to */ + /* multiply by NANOSEC without overflowing, we use 128-bit math instead. */ + return ((uint64_t) counter.LowPart * NANOSEC / uv_hrtime_frequency_) + + (((uint64_t) counter.HighPart * NANOSEC / uv_hrtime_frequency_) + << 32); +} + + +static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) { + if (a->due < b->due) + return -1; + if (a->due > b->due) + return 1; + if ((intptr_t)a < (intptr_t)b) + return -1; + if ((intptr_t)a > (intptr_t)b) + return 1; + return 0; +} + + +RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare); + + +int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { + loop->counters.handle_init++; + loop->counters.timer_init++; + + handle->type = UV_TIMER; + handle->loop = loop; + handle->flags = 0; + handle->timer_cb = NULL; + handle->repeat = 0; + + uv_ref(loop); + + return 0; +} + + +void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, int64_t timeout, + int64_t repeat) { + uv_loop_t* loop = handle->loop; + + if (handle->flags & UV_HANDLE_ACTIVE) { + RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); + } + + handle->timer_cb = timer_cb; + handle->due = loop->time + timeout; + handle->repeat = repeat; + handle->flags |= UV_HANDLE_ACTIVE; + + if (RB_INSERT(uv_timer_tree_s, &loop->timers, handle) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + + return 0; +} + + +int uv_timer_stop(uv_timer_t* handle) { + uv_loop_t* loop = handle->loop; + + if (!(handle->flags & UV_HANDLE_ACTIVE)) + return 0; + + RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); + + handle->flags &= ~UV_HANDLE_ACTIVE; + + return 0; +} + + +int uv_timer_again(uv_timer_t* handle) { + uv_loop_t* loop = handle->loop; + + /* If timer_cb is NULL that means that the timer was never started. */ + if (!handle->timer_cb) { + uv_set_sys_error(loop, ERROR_INVALID_DATA); + return -1; + } + + if (handle->flags & UV_HANDLE_ACTIVE) { + RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); + handle->flags &= ~UV_HANDLE_ACTIVE; + } + + if (handle->repeat) { + handle->due = loop->time + handle->repeat; + + if (RB_INSERT(uv_timer_tree_s, &loop->timers, handle) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + + handle->flags |= UV_HANDLE_ACTIVE; + } + + return 0; +} + + +void uv_timer_set_repeat(uv_timer_t* handle, int64_t repeat) { + assert(handle->type == UV_TIMER); + handle->repeat = repeat; +} + + +int64_t uv_timer_get_repeat(uv_timer_t* handle) { + assert(handle->type == UV_TIMER); + return handle->repeat; +} + + +DWORD uv_get_poll_timeout(uv_loop_t* loop) { + uv_timer_t* timer; + int64_t delta; + + /* Check if there are any running timers */ + timer = RB_MIN(uv_timer_tree_s, &loop->timers); + if (timer) { + uv_update_time(loop); + + delta = timer->due - loop->time; + if (delta >= UINT_MAX >> 1) { + /* A timeout value of UINT_MAX means infinite, so that's no good. But */ + /* more importantly, there's always the risk that GetTickCount wraps. */ + /* uv_update_time can detect this, but we must make sure that the */ + /* tick counter never overflows twice between two subsequent */ + /* uv_update_time calls. We do this by never sleeping more than half */ + /* the time it takes to wrap the counter - which is huge overkill, */ + /* but hey, it's not so bad to wake up every 25 days. */ + return UINT_MAX >> 1; + } else if (delta < 0) { + /* Negative timeout values are not allowed */ + return 0; + } else { + return (DWORD)delta; + } + } else { + /* No timers */ + return INFINITE; + } +} + + +void uv_process_timers(uv_loop_t* loop) { + uv_timer_t* timer; + + /* Call timer callbacks */ + for (timer = RB_MIN(uv_timer_tree_s, &loop->timers); + timer != NULL && timer->due <= loop->time; + timer = RB_MIN(uv_timer_tree_s, &loop->timers)) { + RB_REMOVE(uv_timer_tree_s, &loop->timers, timer); + + if (timer->repeat != 0) { + /* If it is a repeating timer, reschedule with repeat timeout. */ + timer->due += timer->repeat; + if (timer->due < loop->time) { + timer->due = loop->time; + } + if (RB_INSERT(uv_timer_tree_s, &loop->timers, timer) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + } else { + /* If non-repeating, mark the timer as inactive. */ + timer->flags &= ~UV_HANDLE_ACTIVE; + } + + timer->timer_cb((uv_timer_t*) timer, 0); + } +} diff --git a/src/rt/libuv/src/win/tty.c b/src/rt/libuv/src/win/tty.c new file mode 100644 index 00000000000..a4a46829643 --- /dev/null +++ b/src/rt/libuv/src/win/tty.c @@ -0,0 +1,61 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include + + +int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd) { + assert(0 && "implement me"); + return -1; +} + + +int uv_tty_set_mode(uv_tty_t* tty, int mode) { + assert(0 && "implement me"); + return -1; +} + + +int uv_is_tty(uv_file file) { +} + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + assert(0 && "implement me"); + return -1; +} + + +uv_handle_type uv_guess_handle(uv_file file) { + DWORD result; + int r = GetConsoleMode((HANDLE)_get_osfhandle(file), &result); + + if (r) { + return UV_TTY; + } + + assert(0 && "implement me"); + + return UV_UNKNOWN_HANDLE; +} diff --git a/src/rt/libuv/src/win/udp.c b/src/rt/libuv/src/win/udp.c new file mode 100644 index 00000000000..3a2ab6bc1b4 --- /dev/null +++ b/src/rt/libuv/src/win/udp.c @@ -0,0 +1,598 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" +#include + +#if 0 +/* + * Threshold of active udp streams for which to preallocate udp read buffers. + */ +const unsigned int uv_active_udp_streams_threshold = 0; + +/* A zero-size buffer for use by uv_udp_read */ +static char uv_zero_[] = ""; +#endif + +/* Counter to keep track of active udp streams */ +static unsigned int active_udp_streams = 0; + + +int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, + int* namelen) { + uv_loop_t* loop = handle->loop; + int result; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + result = getsockname(handle->socket, name, namelen); + if (result != 0) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, + SOCKET socket) { + DWORD yes = 1; + + assert(handle->socket == INVALID_SOCKET); + + /* Set the socket to nonblocking mode */ + if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + /* Make the socket non-inheritable */ + if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* Associate it with the I/O completion port. */ + /* Use uv_handle_t pointer as completion key. */ + if (CreateIoCompletionPort((HANDLE)socket, + loop->iocp, + (ULONG_PTR)socket, + 0) == NULL) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (pSetFileCompletionNotificationModes) { + if (!pSetFileCompletionNotificationModes((HANDLE)socket, + FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; + } + + handle->socket = socket; + + return 0; +} + + +int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { + handle->type = UV_UDP; + handle->socket = INVALID_SOCKET; + handle->reqs_pending = 0; + handle->loop = loop; + handle->flags = 0; + + uv_req_init(loop, (uv_req_t*) &(handle->recv_req)); + handle->recv_req.type = UV_UDP_RECV; + handle->recv_req.data = handle; + + uv_ref(loop); + + loop->counters.handle_init++; + loop->counters.udp_init++; + + return 0; +} + + +void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING && + handle->reqs_pending == 0) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +static int uv__bind(uv_udp_t* handle, int domain, struct sockaddr* addr, + int addrsize, unsigned int flags) { + uv_loop_t* loop = handle->loop; + DWORD err; + int r; + SOCKET sock; + + if ((flags & UV_UDP_IPV6ONLY) && domain != AF_INET6) { + /* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */ + uv_set_sys_error(loop, UV_EINVAL); + } + + if (handle->socket == INVALID_SOCKET) { + sock = socket(domain, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + if (uv_udp_set_socket(loop, handle, sock) == -1) { + closesocket(sock); + return -1; + } + } + + if (domain == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) { + DWORD off = 0; + /* On windows IPV6ONLY is on by default. */ + /* If the user doesn't specify it libuv turns it off. */ + + /* TODO: how to handle errors? This may fail if there is no ipv4 stack */ + /* available, or when run on XP/2003 which have no support for dualstack */ + /* sockets. For now we're silently ignoring the error. */ + setsockopt(sock, + IPPROTO_IPV6, + IPV6_V6ONLY, + (const char*) &off, + sizeof off); + } + + r = bind(handle->socket, addr, addrsize); + + if (r == SOCKET_ERROR) { + err = WSAGetLastError(); + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + handle->flags |= UV_HANDLE_BOUND; + + return 0; +} + + +int uv_udp_bind(uv_udp_t* handle, struct sockaddr_in addr, + unsigned int flags) { + uv_loop_t* loop = handle->loop; + + if (addr.sin_family != AF_INET) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + return uv__bind(handle, + AF_INET, + (struct sockaddr*) &addr, + sizeof(struct sockaddr_in), + flags); +} + + +int uv_udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, + unsigned int flags) { + uv_loop_t* loop = handle->loop; + + if (addr.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (uv_allow_ipv6) { + handle->flags |= UV_HANDLE_IPV6; + return uv__bind(handle, + AF_INET6, + (struct sockaddr*) &addr, + sizeof(struct sockaddr_in6), + flags); + } else { + uv_new_sys_error(WSAEAFNOSUPPORT); + return -1; + } +} + + +static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { + uv_req_t* req; + uv_buf_t buf; + DWORD bytes, flags; + int result; + + assert(handle->flags & UV_HANDLE_READING); + assert(!(handle->flags & UV_HANDLE_READ_PENDING)); + + req = &handle->recv_req; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + /* + * Preallocate a read buffer if the number of active streams is below + * the threshold. + */ +#if 0 + if (active_udp_streams < uv_active_udp_streams_threshold) { + handle->flags &= ~UV_HANDLE_ZERO_READ; +#endif + handle->recv_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(handle->recv_buffer.len > 0); + + buf = handle->recv_buffer; + memset(&handle->recv_from, 0, sizeof handle->recv_from); + handle->recv_from_len = sizeof handle->recv_from; + flags = 0; + + result = WSARecvFrom(handle->socket, + (WSABUF*) &buf, + 1, + &bytes, + &flags, + (struct sockaddr*) &handle->recv_from, + &handle->recv_from_len, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->overlapped.InternalHigh = bytes; + handle->reqs_pending++; + uv_insert_pending_req(loop, req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* The req will be processed with IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; + } else { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv_insert_pending_req(loop, req); + handle->reqs_pending++; + } +#if 0 + } else { + handle->flags |= UV_HANDLE_ZERO_READ; + + buf.base = (char*) uv_zero_; + buf.len = 0; + flags = MSG_PARTIAL; + + result = WSARecv(handle->socket, + (WSABUF*) &buf, + 1, + &bytes, + &flags, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->overlapped.InternalHigh = bytes; + handle->reqs_pending++; + uv_insert_pending_req(loop, req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* The req will be processed with IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; + } else { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv_insert_pending_req(loop, req); + handle->reqs_pending++; + } + } +#endif +} + + +int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, + uv_udp_recv_cb recv_cb) { + uv_loop_t* loop = handle->loop; + + if (handle->flags & UV_HANDLE_READING) { + uv_set_sys_error(loop, WSAEALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind(handle, uv_addr_ip4_any_, 0) < 0) { + return -1; + } + + handle->flags |= UV_HANDLE_READING; + active_udp_streams++; + + handle->recv_cb = recv_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell be a */ + /* recv request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + uv_udp_queue_recv(loop, handle); + + return 0; +} + + +int uv_udp_recv_stop(uv_udp_t* handle) { + if (handle->flags & UV_HANDLE_READING) { + handle->flags &= ~UV_HANDLE_READING; + active_udp_streams--; + } + + return 0; +} + + +int uv_udp_connect6(uv_connect_t* req, uv_udp_t* handle, + struct sockaddr_in6 address, uv_connect_cb cb) { + uv_loop_t* loop = handle->loop; + int addrsize = sizeof(struct sockaddr_in6); + BOOL success; + DWORD bytes; + + if (!uv_allow_ipv6) { + uv_new_sys_error(WSAEAFNOSUPPORT); + return -1; + } + + if (address.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) + return -1; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx6(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(success)) { + handle->reqs_pending++; + } else { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr* addr, int addr_len, uv_udp_send_cb cb) { + uv_loop_t* loop = handle->loop; + DWORD result, bytes; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_UDP_SEND; + req->handle = handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + result = WSASendTo(handle->socket, + (WSABUF*)bufs, + bufcnt, + &bytes, + 0, + addr, + addr_len, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Request completed immediately. */ + req->queued_bytes = 0; + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->reqs_pending++; + } else { + /* Send failed due to an error. */ + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in addr, uv_udp_send_cb cb) { + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind(handle, uv_addr_ip4_any_, 0) < 0) { + return -1; + } + + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*) &addr, + sizeof addr, + cb); +} + + +int uv_udp_send6(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in6 addr, uv_udp_send_cb cb) { + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) { + return -1; + } + + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*) &addr, + sizeof addr, + cb); +} + + +void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, + uv_req_t* req) { + uv_buf_t buf; + int partial; + + assert(handle->type == UV_UDP); + + handle->flags &= ~UV_HANDLE_READ_PENDING; + + if (!REQ_SUCCESS(req) && + GET_REQ_STATUS(req) != STATUS_RECEIVE_EXPEDITED) { + /* An error occurred doing the read. */ + if ((handle->flags & UV_HANDLE_READING)) { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + uv_udp_recv_stop(handle); +#if 0 + buf = (handle->flags & UV_HANDLE_ZERO_READ) ? + uv_buf_init(NULL, 0) : handle->recv_buffer; +#else + buf = handle->recv_buffer; +#endif + handle->recv_cb(handle, -1, buf, NULL, 0); + } + goto done; + } + +#if 0 + if (!(handle->flags & UV_HANDLE_ZERO_READ)) { +#endif + /* Successful read */ + partial = (GET_REQ_STATUS(req) == STATUS_RECEIVE_EXPEDITED); + handle->recv_cb(handle, + req->overlapped.InternalHigh, + handle->recv_buffer, + (struct sockaddr*) &handle->recv_from, + partial ? UV_UDP_PARTIAL : 0); +#if 0 + } else { + DWORD bytes, err, flags; + struct sockaddr_storage from; + int from_len; + + /* Do a nonblocking receive */ + /* TODO: try to read multiple datagrams at once. FIONREAD maybe? */ + buf = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(buf.len > 0); + + memset(&from, 0, sizeof from); + from_len = sizeof from; + flags = MSG_PARTIAL; + + if (WSARecvFrom(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + (struct sockaddr*) &from, + &from_len, + NULL, + NULL) != SOCKET_ERROR) { + + /* Message received */ + handle->recv_cb(handle, + bytes, + buf, + (struct sockaddr*) &from, + (flags & MSG_PARTIAL) ? UV_UDP_PARTIAL : 0); + } else { + err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { + uv_set_sys_error(loop, WSAEWOULDBLOCK); + handle->recv_cb(handle, 0, buf, NULL, 0); + } else { + /* Ouch! serious error. */ + uv_set_sys_error(loop, err); + handle->recv_cb(handle, -1, buf, NULL, 0); + } + } + } +#endif + +done: + /* Post another read if still reading and not closing. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + uv_udp_queue_recv(loop, handle); + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + + +void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, + uv_udp_send_t* req) { + assert(handle->type == UV_UDP); + + if (req->cb) { + if (REQ_SUCCESS(req)) { + req->cb(req, 0); + } else { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + req->cb(req, -1); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + diff --git a/src/rt/libuv/src/win/util.c b/src/rt/libuv/src/win/util.c new file mode 100644 index 00000000000..67de53e5c04 --- /dev/null +++ b/src/rt/libuv/src/win/util.c @@ -0,0 +1,96 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "uv.h" +#include "internal.h" + + +int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, + char* utf8Buffer, size_t utf8Size) { + return WideCharToMultiByte(CP_UTF8, + 0, + utf16Buffer, + utf16Size, + utf8Buffer, + utf8Size, + NULL, + NULL); +} + + +int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, + size_t utf16Size) { + return MultiByteToWideChar(CP_UTF8, + 0, + utf8Buffer, + -1, + utf16Buffer, + utf16Size); +} + + +int uv_exepath(char* buffer, size_t* size) { + int retVal; + size_t utf16Size; + wchar_t* utf16Buffer; + + if (!buffer || !size) { + return -1; + } + + utf16Buffer = (wchar_t*)malloc(sizeof(wchar_t) * *size); + if (!utf16Buffer) { + retVal = -1; + goto done; + } + + /* Get the path as UTF-16 */ + utf16Size = GetModuleFileNameW(NULL, utf16Buffer, *size - 1); + if (utf16Size <= 0) { + /* uv_set_sys_error(loop, GetLastError()); */ + retVal = -1; + goto done; + } + + utf16Buffer[utf16Size] = L'\0'; + + /* Convert to UTF-8 */ + *size = uv_utf16_to_utf8(utf16Buffer, utf16Size, buffer, *size); + if (!*size) { + /* uv_set_sys_error(loop, GetLastError()); */ + retVal = -1; + goto done; + } + + buffer[*size] = '\0'; + retVal = 0; + +done: + if (utf16Buffer) { + free(utf16Buffer); + } + + return retVal; +} diff --git a/src/rt/libuv/src/win/winapi.c b/src/rt/libuv/src/win/winapi.c new file mode 100644 index 00000000000..4a58c14574f --- /dev/null +++ b/src/rt/libuv/src/win/winapi.c @@ -0,0 +1,81 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +sRtlNtStatusToDosError pRtlNtStatusToDosError; +sNtQueryInformationFile pNtQueryInformationFile; +sNtSetInformationFile pNtSetInformationFile; +sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; +sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; +sCreateSymbolicLinkA pCreateSymbolicLinkA; + + +void uv_winapi_init() { + HMODULE ntdll_module; + HMODULE kernel32_module; + + ntdll_module = GetModuleHandleA("ntdll.dll"); + if (ntdll_module == NULL) { + uv_fatal_error(GetLastError(), "GetModuleHandleA"); + } + + pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress( + ntdll_module, + "RtlNtStatusToDosError"); + if (pRtlNtStatusToDosError == NULL) { + uv_fatal_error(GetLastError(), "GetProcAddress"); + } + + pNtQueryInformationFile = (sNtQueryInformationFile) GetProcAddress( + ntdll_module, + "NtQueryInformationFile"); + if (pNtQueryInformationFile == NULL) { + uv_fatal_error(GetLastError(), "GetProcAddress"); + } + + pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress( + ntdll_module, + "NtSetInformationFile"); + if (pNtSetInformationFile == NULL) { + uv_fatal_error(GetLastError(), "GetProcAddress"); + } + + kernel32_module = GetModuleHandleA("kernel32.dll"); + if (kernel32_module == NULL) { + uv_fatal_error(GetLastError(), "GetModuleHandleA"); + } + + pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress( + kernel32_module, + "GetQueuedCompletionStatusEx"); + + pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes) + GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes"); + + pCreateSymbolicLinkA = (sCreateSymbolicLinkA) + GetProcAddress(kernel32_module, "CreateSymbolicLinkA"); +} diff --git a/src/rt/libuv/src/win/winapi.h b/src/rt/libuv/src/win/winapi.h new file mode 100644 index 00000000000..760281e8619 --- /dev/null +++ b/src/rt/libuv/src/win/winapi.h @@ -0,0 +1,4337 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_WIN_WINAPI_H_ +#define UV_WIN_WINAPI_H_ + +#include + + +/* + * Ntdll headers + */ +#ifndef _NTDEF_ + typedef LONG NTSTATUS; + typedef NTSTATUS *PNTSTATUS; +#endif + +#ifndef STATUS_SEVERITY_SUCCESS +# define STATUS_SEVERITY_SUCCESS 0x0 +#endif + +#ifndef STATUS_SEVERITY_INFORMATIONAL +# define STATUS_SEVERITY_INFORMATIONAL 0x1 +#endif + +#ifndef STATUS_SEVERITY_WARNING +# define STATUS_SEVERITY_WARNING 0x2 +#endif + +#ifndef STATUS_SEVERITY_ERROR +# define STATUS_SEVERITY_ERROR 0x3 +#endif + +#ifndef FACILITY_NTWIN32 +# define FACILITY_NTWIN32 0x7 +#endif + +#ifndef NT_SUCCESS +# define NT_SUCCESS(status) (((NTSTATUS) (status)) >= 0) +#endif + +#ifndef STATUS_SUCCESS +# define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) +#endif + +#ifndef STATUS_WAIT_0 +# define STATUS_WAIT_0 ((NTSTATUS) 0x00000000L) +#endif + +#ifndef STATUS_WAIT_1 +# define STATUS_WAIT_1 ((NTSTATUS) 0x00000001L) +#endif + +#ifndef STATUS_WAIT_2 +# define STATUS_WAIT_2 ((NTSTATUS) 0x00000002L) +#endif + +#ifndef STATUS_WAIT_3 +# define STATUS_WAIT_3 ((NTSTATUS) 0x00000003L) +#endif + +#ifndef STATUS_WAIT_63 +# define STATUS_WAIT_63 ((NTSTATUS) 0x0000003FL) +#endif + +#ifndef STATUS_ABANDONED +# define STATUS_ABANDONED ((NTSTATUS) 0x00000080L) +#endif + +#ifndef STATUS_ABANDONED_WAIT_0 +# define STATUS_ABANDONED_WAIT_0 ((NTSTATUS) 0x00000080L) +#endif + +#ifndef STATUS_ABANDONED_WAIT_63 +# define STATUS_ABANDONED_WAIT_63 ((NTSTATUS) 0x000000BFL) +#endif + +#ifndef STATUS_USER_APC +# define STATUS_USER_APC ((NTSTATUS) 0x000000C0L) +#endif + +#ifndef STATUS_KERNEL_APC +# define STATUS_KERNEL_APC ((NTSTATUS) 0x00000100L) +#endif + +#ifndef STATUS_ALERTED +# define STATUS_ALERTED ((NTSTATUS) 0x00000101L) +#endif + +#ifndef STATUS_TIMEOUT +# define STATUS_TIMEOUT ((NTSTATUS) 0x00000102L) +#endif + +#ifndef STATUS_PENDING +# define STATUS_PENDING ((NTSTATUS) 0x00000103L) +#endif + +#ifndef STATUS_REPARSE +# define STATUS_REPARSE ((NTSTATUS) 0x00000104L) +#endif + +#ifndef STATUS_MORE_ENTRIES +# define STATUS_MORE_ENTRIES ((NTSTATUS) 0x00000105L) +#endif + +#ifndef STATUS_NOT_ALL_ASSIGNED +# define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS) 0x00000106L) +#endif + +#ifndef STATUS_SOME_NOT_MAPPED +# define STATUS_SOME_NOT_MAPPED ((NTSTATUS) 0x00000107L) +#endif + +#ifndef STATUS_OPLOCK_BREAK_IN_PROGRESS +# define STATUS_OPLOCK_BREAK_IN_PROGRESS ((NTSTATUS) 0x00000108L) +#endif + +#ifndef STATUS_VOLUME_MOUNTED +# define STATUS_VOLUME_MOUNTED ((NTSTATUS) 0x00000109L) +#endif + +#ifndef STATUS_RXACT_COMMITTED +# define STATUS_RXACT_COMMITTED ((NTSTATUS) 0x0000010AL) +#endif + +#ifndef STATUS_NOTIFY_CLEANUP +# define STATUS_NOTIFY_CLEANUP ((NTSTATUS) 0x0000010BL) +#endif + +#ifndef STATUS_NOTIFY_ENUM_DIR +# define STATUS_NOTIFY_ENUM_DIR ((NTSTATUS) 0x0000010CL) +#endif + +#ifndef STATUS_NO_QUOTAS_FOR_ACCOUNT +# define STATUS_NO_QUOTAS_FOR_ACCOUNT ((NTSTATUS) 0x0000010DL) +#endif + +#ifndef STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED +# define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED ((NTSTATUS) 0x0000010EL) +#endif + +#ifndef STATUS_PAGE_FAULT_TRANSITION +# define STATUS_PAGE_FAULT_TRANSITION ((NTSTATUS) 0x00000110L) +#endif + +#ifndef STATUS_PAGE_FAULT_DEMAND_ZERO +# define STATUS_PAGE_FAULT_DEMAND_ZERO ((NTSTATUS) 0x00000111L) +#endif + +#ifndef STATUS_PAGE_FAULT_COPY_ON_WRITE +# define STATUS_PAGE_FAULT_COPY_ON_WRITE ((NTSTATUS) 0x00000112L) +#endif + +#ifndef STATUS_PAGE_FAULT_GUARD_PAGE +# define STATUS_PAGE_FAULT_GUARD_PAGE ((NTSTATUS) 0x00000113L) +#endif + +#ifndef STATUS_PAGE_FAULT_PAGING_FILE +# define STATUS_PAGE_FAULT_PAGING_FILE ((NTSTATUS) 0x00000114L) +#endif + +#ifndef STATUS_CACHE_PAGE_LOCKED +# define STATUS_CACHE_PAGE_LOCKED ((NTSTATUS) 0x00000115L) +#endif + +#ifndef STATUS_CRASH_DUMP +# define STATUS_CRASH_DUMP ((NTSTATUS) 0x00000116L) +#endif + +#ifndef STATUS_BUFFER_ALL_ZEROS +# define STATUS_BUFFER_ALL_ZEROS ((NTSTATUS) 0x00000117L) +#endif + +#ifndef STATUS_REPARSE_OBJECT +# define STATUS_REPARSE_OBJECT ((NTSTATUS) 0x00000118L) +#endif + +#ifndef STATUS_RESOURCE_REQUIREMENTS_CHANGED +# define STATUS_RESOURCE_REQUIREMENTS_CHANGED ((NTSTATUS) 0x00000119L) +#endif + +#ifndef STATUS_TRANSLATION_COMPLETE +# define STATUS_TRANSLATION_COMPLETE ((NTSTATUS) 0x00000120L) +#endif + +#ifndef STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY +# define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY ((NTSTATUS) 0x00000121L) +#endif + +#ifndef STATUS_NOTHING_TO_TERMINATE +# define STATUS_NOTHING_TO_TERMINATE ((NTSTATUS) 0x00000122L) +#endif + +#ifndef STATUS_PROCESS_NOT_IN_JOB +# define STATUS_PROCESS_NOT_IN_JOB ((NTSTATUS) 0x00000123L) +#endif + +#ifndef STATUS_PROCESS_IN_JOB +# define STATUS_PROCESS_IN_JOB ((NTSTATUS) 0x00000124L) +#endif + +#ifndef STATUS_VOLSNAP_HIBERNATE_READY +# define STATUS_VOLSNAP_HIBERNATE_READY ((NTSTATUS) 0x00000125L) +#endif + +#ifndef STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY +# define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY ((NTSTATUS) 0x00000126L) +#endif + +#ifndef STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED +# define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED ((NTSTATUS) 0x00000127L) +#endif + +#ifndef STATUS_INTERRUPT_STILL_CONNECTED +# define STATUS_INTERRUPT_STILL_CONNECTED ((NTSTATUS) 0x00000128L) +#endif + +#ifndef STATUS_PROCESS_CLONED +# define STATUS_PROCESS_CLONED ((NTSTATUS) 0x00000129L) +#endif + +#ifndef STATUS_FILE_LOCKED_WITH_ONLY_READERS +# define STATUS_FILE_LOCKED_WITH_ONLY_READERS ((NTSTATUS) 0x0000012AL) +#endif + +#ifndef STATUS_FILE_LOCKED_WITH_WRITERS +# define STATUS_FILE_LOCKED_WITH_WRITERS ((NTSTATUS) 0x0000012BL) +#endif + +#ifndef STATUS_RESOURCEMANAGER_READ_ONLY +# define STATUS_RESOURCEMANAGER_READ_ONLY ((NTSTATUS) 0x00000202L) +#endif + +#ifndef STATUS_RING_PREVIOUSLY_EMPTY +# define STATUS_RING_PREVIOUSLY_EMPTY ((NTSTATUS) 0x00000210L) +#endif + +#ifndef STATUS_RING_PREVIOUSLY_FULL +# define STATUS_RING_PREVIOUSLY_FULL ((NTSTATUS) 0x00000211L) +#endif + +#ifndef STATUS_RING_PREVIOUSLY_ABOVE_QUOTA +# define STATUS_RING_PREVIOUSLY_ABOVE_QUOTA ((NTSTATUS) 0x00000212L) +#endif + +#ifndef STATUS_RING_NEWLY_EMPTY +# define STATUS_RING_NEWLY_EMPTY ((NTSTATUS) 0x00000213L) +#endif + +#ifndef STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT +# define STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT ((NTSTATUS) 0x00000214L) +#endif + +#ifndef STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE +# define STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE ((NTSTATUS) 0x00000215L) +#endif + +#ifndef STATUS_OPLOCK_HANDLE_CLOSED +# define STATUS_OPLOCK_HANDLE_CLOSED ((NTSTATUS) 0x00000216L) +#endif + +#ifndef STATUS_WAIT_FOR_OPLOCK +# define STATUS_WAIT_FOR_OPLOCK ((NTSTATUS) 0x00000367L) +#endif + +#ifndef STATUS_OBJECT_NAME_EXISTS +# define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS) 0x40000000L) +#endif + +#ifndef STATUS_THREAD_WAS_SUSPENDED +# define STATUS_THREAD_WAS_SUSPENDED ((NTSTATUS) 0x40000001L) +#endif + +#ifndef STATUS_WORKING_SET_LIMIT_RANGE +# define STATUS_WORKING_SET_LIMIT_RANGE ((NTSTATUS) 0x40000002L) +#endif + +#ifndef STATUS_IMAGE_NOT_AT_BASE +# define STATUS_IMAGE_NOT_AT_BASE ((NTSTATUS) 0x40000003L) +#endif + +#ifndef STATUS_RXACT_STATE_CREATED +# define STATUS_RXACT_STATE_CREATED ((NTSTATUS) 0x40000004L) +#endif + +#ifndef STATUS_SEGMENT_NOTIFICATION +# define STATUS_SEGMENT_NOTIFICATION ((NTSTATUS) 0x40000005L) +#endif + +#ifndef STATUS_LOCAL_USER_SESSION_KEY +# define STATUS_LOCAL_USER_SESSION_KEY ((NTSTATUS) 0x40000006L) +#endif + +#ifndef STATUS_BAD_CURRENT_DIRECTORY +# define STATUS_BAD_CURRENT_DIRECTORY ((NTSTATUS) 0x40000007L) +#endif + +#ifndef STATUS_SERIAL_MORE_WRITES +# define STATUS_SERIAL_MORE_WRITES ((NTSTATUS) 0x40000008L) +#endif + +#ifndef STATUS_REGISTRY_RECOVERED +# define STATUS_REGISTRY_RECOVERED ((NTSTATUS) 0x40000009L) +#endif + +#ifndef STATUS_FT_READ_RECOVERY_FROM_BACKUP +# define STATUS_FT_READ_RECOVERY_FROM_BACKUP ((NTSTATUS) 0x4000000AL) +#endif + +#ifndef STATUS_FT_WRITE_RECOVERY +# define STATUS_FT_WRITE_RECOVERY ((NTSTATUS) 0x4000000BL) +#endif + +#ifndef STATUS_SERIAL_COUNTER_TIMEOUT +# define STATUS_SERIAL_COUNTER_TIMEOUT ((NTSTATUS) 0x4000000CL) +#endif + +#ifndef STATUS_NULL_LM_PASSWORD +# define STATUS_NULL_LM_PASSWORD ((NTSTATUS) 0x4000000DL) +#endif + +#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH +# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH ((NTSTATUS) 0x4000000EL) +#endif + +#ifndef STATUS_RECEIVE_PARTIAL +# define STATUS_RECEIVE_PARTIAL ((NTSTATUS) 0x4000000FL) +#endif + +#ifndef STATUS_RECEIVE_EXPEDITED +# define STATUS_RECEIVE_EXPEDITED ((NTSTATUS) 0x40000010L) +#endif + +#ifndef STATUS_RECEIVE_PARTIAL_EXPEDITED +# define STATUS_RECEIVE_PARTIAL_EXPEDITED ((NTSTATUS) 0x40000011L) +#endif + +#ifndef STATUS_EVENT_DONE +# define STATUS_EVENT_DONE ((NTSTATUS) 0x40000012L) +#endif + +#ifndef STATUS_EVENT_PENDING +# define STATUS_EVENT_PENDING ((NTSTATUS) 0x40000013L) +#endif + +#ifndef STATUS_CHECKING_FILE_SYSTEM +# define STATUS_CHECKING_FILE_SYSTEM ((NTSTATUS) 0x40000014L) +#endif + +#ifndef STATUS_FATAL_APP_EXIT +# define STATUS_FATAL_APP_EXIT ((NTSTATUS) 0x40000015L) +#endif + +#ifndef STATUS_PREDEFINED_HANDLE +# define STATUS_PREDEFINED_HANDLE ((NTSTATUS) 0x40000016L) +#endif + +#ifndef STATUS_WAS_UNLOCKED +# define STATUS_WAS_UNLOCKED ((NTSTATUS) 0x40000017L) +#endif + +#ifndef STATUS_SERVICE_NOTIFICATION +# define STATUS_SERVICE_NOTIFICATION ((NTSTATUS) 0x40000018L) +#endif + +#ifndef STATUS_WAS_LOCKED +# define STATUS_WAS_LOCKED ((NTSTATUS) 0x40000019L) +#endif + +#ifndef STATUS_LOG_HARD_ERROR +# define STATUS_LOG_HARD_ERROR ((NTSTATUS) 0x4000001AL) +#endif + +#ifndef STATUS_ALREADY_WIN32 +# define STATUS_ALREADY_WIN32 ((NTSTATUS) 0x4000001BL) +#endif + +#ifndef STATUS_WX86_UNSIMULATE +# define STATUS_WX86_UNSIMULATE ((NTSTATUS) 0x4000001CL) +#endif + +#ifndef STATUS_WX86_CONTINUE +# define STATUS_WX86_CONTINUE ((NTSTATUS) 0x4000001DL) +#endif + +#ifndef STATUS_WX86_SINGLE_STEP +# define STATUS_WX86_SINGLE_STEP ((NTSTATUS) 0x4000001EL) +#endif + +#ifndef STATUS_WX86_BREAKPOINT +# define STATUS_WX86_BREAKPOINT ((NTSTATUS) 0x4000001FL) +#endif + +#ifndef STATUS_WX86_EXCEPTION_CONTINUE +# define STATUS_WX86_EXCEPTION_CONTINUE ((NTSTATUS) 0x40000020L) +#endif + +#ifndef STATUS_WX86_EXCEPTION_LASTCHANCE +# define STATUS_WX86_EXCEPTION_LASTCHANCE ((NTSTATUS) 0x40000021L) +#endif + +#ifndef STATUS_WX86_EXCEPTION_CHAIN +# define STATUS_WX86_EXCEPTION_CHAIN ((NTSTATUS) 0x40000022L) +#endif + +#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE +# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE ((NTSTATUS) 0x40000023L) +#endif + +#ifndef STATUS_NO_YIELD_PERFORMED +# define STATUS_NO_YIELD_PERFORMED ((NTSTATUS) 0x40000024L) +#endif + +#ifndef STATUS_TIMER_RESUME_IGNORED +# define STATUS_TIMER_RESUME_IGNORED ((NTSTATUS) 0x40000025L) +#endif + +#ifndef STATUS_ARBITRATION_UNHANDLED +# define STATUS_ARBITRATION_UNHANDLED ((NTSTATUS) 0x40000026L) +#endif + +#ifndef STATUS_CARDBUS_NOT_SUPPORTED +# define STATUS_CARDBUS_NOT_SUPPORTED ((NTSTATUS) 0x40000027L) +#endif + +#ifndef STATUS_WX86_CREATEWX86TIB +# define STATUS_WX86_CREATEWX86TIB ((NTSTATUS) 0x40000028L) +#endif + +#ifndef STATUS_MP_PROCESSOR_MISMATCH +# define STATUS_MP_PROCESSOR_MISMATCH ((NTSTATUS) 0x40000029L) +#endif + +#ifndef STATUS_HIBERNATED +# define STATUS_HIBERNATED ((NTSTATUS) 0x4000002AL) +#endif + +#ifndef STATUS_RESUME_HIBERNATION +# define STATUS_RESUME_HIBERNATION ((NTSTATUS) 0x4000002BL) +#endif + +#ifndef STATUS_FIRMWARE_UPDATED +# define STATUS_FIRMWARE_UPDATED ((NTSTATUS) 0x4000002CL) +#endif + +#ifndef STATUS_DRIVERS_LEAKING_LOCKED_PAGES +# define STATUS_DRIVERS_LEAKING_LOCKED_PAGES ((NTSTATUS) 0x4000002DL) +#endif + +#ifndef STATUS_MESSAGE_RETRIEVED +# define STATUS_MESSAGE_RETRIEVED ((NTSTATUS) 0x4000002EL) +#endif + +#ifndef STATUS_SYSTEM_POWERSTATE_TRANSITION +# define STATUS_SYSTEM_POWERSTATE_TRANSITION ((NTSTATUS) 0x4000002FL) +#endif + +#ifndef STATUS_ALPC_CHECK_COMPLETION_LIST +# define STATUS_ALPC_CHECK_COMPLETION_LIST ((NTSTATUS) 0x40000030L) +#endif + +#ifndef STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION +# define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION ((NTSTATUS) 0x40000031L) +#endif + +#ifndef STATUS_ACCESS_AUDIT_BY_POLICY +# define STATUS_ACCESS_AUDIT_BY_POLICY ((NTSTATUS) 0x40000032L) +#endif + +#ifndef STATUS_ABANDON_HIBERFILE +# define STATUS_ABANDON_HIBERFILE ((NTSTATUS) 0x40000033L) +#endif + +#ifndef STATUS_BIZRULES_NOT_ENABLED +# define STATUS_BIZRULES_NOT_ENABLED ((NTSTATUS) 0x40000034L) +#endif + +#ifndef STATUS_GUARD_PAGE_VIOLATION +# define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS) 0x80000001L) +#endif + +#ifndef STATUS_DATATYPE_MISALIGNMENT +# define STATUS_DATATYPE_MISALIGNMENT ((NTSTATUS) 0x80000002L) +#endif + +#ifndef STATUS_BREAKPOINT +# define STATUS_BREAKPOINT ((NTSTATUS) 0x80000003L) +#endif + +#ifndef STATUS_SINGLE_STEP +# define STATUS_SINGLE_STEP ((NTSTATUS) 0x80000004L) +#endif + +#ifndef STATUS_BUFFER_OVERFLOW +# define STATUS_BUFFER_OVERFLOW ((NTSTATUS) 0x80000005L) +#endif + +#ifndef STATUS_NO_MORE_FILES +# define STATUS_NO_MORE_FILES ((NTSTATUS) 0x80000006L) +#endif + +#ifndef STATUS_WAKE_SYSTEM_DEBUGGER +# define STATUS_WAKE_SYSTEM_DEBUGGER ((NTSTATUS) 0x80000007L) +#endif + +#ifndef STATUS_HANDLES_CLOSED +# define STATUS_HANDLES_CLOSED ((NTSTATUS) 0x8000000AL) +#endif + +#ifndef STATUS_NO_INHERITANCE +# define STATUS_NO_INHERITANCE ((NTSTATUS) 0x8000000BL) +#endif + +#ifndef STATUS_GUID_SUBSTITUTION_MADE +# define STATUS_GUID_SUBSTITUTION_MADE ((NTSTATUS) 0x8000000CL) +#endif + +#ifndef STATUS_PARTIAL_COPY +# define STATUS_PARTIAL_COPY ((NTSTATUS) 0x8000000DL) +#endif + +#ifndef STATUS_DEVICE_PAPER_EMPTY +# define STATUS_DEVICE_PAPER_EMPTY ((NTSTATUS) 0x8000000EL) +#endif + +#ifndef STATUS_DEVICE_POWERED_OFF +# define STATUS_DEVICE_POWERED_OFF ((NTSTATUS) 0x8000000FL) +#endif + +#ifndef STATUS_DEVICE_OFF_LINE +# define STATUS_DEVICE_OFF_LINE ((NTSTATUS) 0x80000010L) +#endif + +#ifndef STATUS_DEVICE_BUSY +# define STATUS_DEVICE_BUSY ((NTSTATUS) 0x80000011L) +#endif + +#ifndef STATUS_NO_MORE_EAS +# define STATUS_NO_MORE_EAS ((NTSTATUS) 0x80000012L) +#endif + +#ifndef STATUS_INVALID_EA_NAME +# define STATUS_INVALID_EA_NAME ((NTSTATUS) 0x80000013L) +#endif + +#ifndef STATUS_EA_LIST_INCONSISTENT +# define STATUS_EA_LIST_INCONSISTENT ((NTSTATUS) 0x80000014L) +#endif + +#ifndef STATUS_INVALID_EA_FLAG +# define STATUS_INVALID_EA_FLAG ((NTSTATUS) 0x80000015L) +#endif + +#ifndef STATUS_VERIFY_REQUIRED +# define STATUS_VERIFY_REQUIRED ((NTSTATUS) 0x80000016L) +#endif + +#ifndef STATUS_EXTRANEOUS_INFORMATION +# define STATUS_EXTRANEOUS_INFORMATION ((NTSTATUS) 0x80000017L) +#endif + +#ifndef STATUS_RXACT_COMMIT_NECESSARY +# define STATUS_RXACT_COMMIT_NECESSARY ((NTSTATUS) 0x80000018L) +#endif + +#ifndef STATUS_NO_MORE_ENTRIES +# define STATUS_NO_MORE_ENTRIES ((NTSTATUS) 0x8000001AL) +#endif + +#ifndef STATUS_FILEMARK_DETECTED +# define STATUS_FILEMARK_DETECTED ((NTSTATUS) 0x8000001BL) +#endif + +#ifndef STATUS_MEDIA_CHANGED +# define STATUS_MEDIA_CHANGED ((NTSTATUS) 0x8000001CL) +#endif + +#ifndef STATUS_BUS_RESET +# define STATUS_BUS_RESET ((NTSTATUS) 0x8000001DL) +#endif + +#ifndef STATUS_END_OF_MEDIA +# define STATUS_END_OF_MEDIA ((NTSTATUS) 0x8000001EL) +#endif + +#ifndef STATUS_BEGINNING_OF_MEDIA +# define STATUS_BEGINNING_OF_MEDIA ((NTSTATUS) 0x8000001FL) +#endif + +#ifndef STATUS_MEDIA_CHECK +# define STATUS_MEDIA_CHECK ((NTSTATUS) 0x80000020L) +#endif + +#ifndef STATUS_SETMARK_DETECTED +# define STATUS_SETMARK_DETECTED ((NTSTATUS) 0x80000021L) +#endif + +#ifndef STATUS_NO_DATA_DETECTED +# define STATUS_NO_DATA_DETECTED ((NTSTATUS) 0x80000022L) +#endif + +#ifndef STATUS_REDIRECTOR_HAS_OPEN_HANDLES +# define STATUS_REDIRECTOR_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000023L) +#endif + +#ifndef STATUS_SERVER_HAS_OPEN_HANDLES +# define STATUS_SERVER_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000024L) +#endif + +#ifndef STATUS_ALREADY_DISCONNECTED +# define STATUS_ALREADY_DISCONNECTED ((NTSTATUS) 0x80000025L) +#endif + +#ifndef STATUS_LONGJUMP +# define STATUS_LONGJUMP ((NTSTATUS) 0x80000026L) +#endif + +#ifndef STATUS_CLEANER_CARTRIDGE_INSTALLED +# define STATUS_CLEANER_CARTRIDGE_INSTALLED ((NTSTATUS) 0x80000027L) +#endif + +#ifndef STATUS_PLUGPLAY_QUERY_VETOED +# define STATUS_PLUGPLAY_QUERY_VETOED ((NTSTATUS) 0x80000028L) +#endif + +#ifndef STATUS_UNWIND_CONSOLIDATE +# define STATUS_UNWIND_CONSOLIDATE ((NTSTATUS) 0x80000029L) +#endif + +#ifndef STATUS_REGISTRY_HIVE_RECOVERED +# define STATUS_REGISTRY_HIVE_RECOVERED ((NTSTATUS) 0x8000002AL) +#endif + +#ifndef STATUS_DLL_MIGHT_BE_INSECURE +# define STATUS_DLL_MIGHT_BE_INSECURE ((NTSTATUS) 0x8000002BL) +#endif + +#ifndef STATUS_DLL_MIGHT_BE_INCOMPATIBLE +# define STATUS_DLL_MIGHT_BE_INCOMPATIBLE ((NTSTATUS) 0x8000002CL) +#endif + +#ifndef STATUS_STOPPED_ON_SYMLINK +# define STATUS_STOPPED_ON_SYMLINK ((NTSTATUS) 0x8000002DL) +#endif + +#ifndef STATUS_CANNOT_GRANT_REQUESTED_OPLOCK +# define STATUS_CANNOT_GRANT_REQUESTED_OPLOCK ((NTSTATUS) 0x8000002EL) +#endif + +#ifndef STATUS_NO_ACE_CONDITION +# define STATUS_NO_ACE_CONDITION ((NTSTATUS) 0x8000002FL) +#endif + +#ifndef STATUS_UNSUCCESSFUL +# define STATUS_UNSUCCESSFUL ((NTSTATUS) 0xC0000001L) +#endif + +#ifndef STATUS_NOT_IMPLEMENTED +# define STATUS_NOT_IMPLEMENTED ((NTSTATUS) 0xC0000002L) +#endif + +#ifndef STATUS_INVALID_INFO_CLASS +# define STATUS_INVALID_INFO_CLASS ((NTSTATUS) 0xC0000003L) +#endif + +#ifndef STATUS_INFO_LENGTH_MISMATCH +# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xC0000004L) +#endif + +#ifndef STATUS_ACCESS_VIOLATION +# define STATUS_ACCESS_VIOLATION ((NTSTATUS) 0xC0000005L) +#endif + +#ifndef STATUS_IN_PAGE_ERROR +# define STATUS_IN_PAGE_ERROR ((NTSTATUS) 0xC0000006L) +#endif + +#ifndef STATUS_PAGEFILE_QUOTA +# define STATUS_PAGEFILE_QUOTA ((NTSTATUS) 0xC0000007L) +#endif + +#ifndef STATUS_INVALID_HANDLE +# define STATUS_INVALID_HANDLE ((NTSTATUS) 0xC0000008L) +#endif + +#ifndef STATUS_BAD_INITIAL_STACK +# define STATUS_BAD_INITIAL_STACK ((NTSTATUS) 0xC0000009L) +#endif + +#ifndef STATUS_BAD_INITIAL_PC +# define STATUS_BAD_INITIAL_PC ((NTSTATUS) 0xC000000AL) +#endif + +#ifndef STATUS_INVALID_CID +# define STATUS_INVALID_CID ((NTSTATUS) 0xC000000BL) +#endif + +#ifndef STATUS_TIMER_NOT_CANCELED +# define STATUS_TIMER_NOT_CANCELED ((NTSTATUS) 0xC000000CL) +#endif + +#ifndef STATUS_INVALID_PARAMETER +# define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xC000000DL) +#endif + +#ifndef STATUS_NO_SUCH_DEVICE +# define STATUS_NO_SUCH_DEVICE ((NTSTATUS) 0xC000000EL) +#endif + +#ifndef STATUS_NO_SUCH_FILE +# define STATUS_NO_SUCH_FILE ((NTSTATUS) 0xC000000FL) +#endif + +#ifndef STATUS_INVALID_DEVICE_REQUEST +# define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS) 0xC0000010L) +#endif + +#ifndef STATUS_END_OF_FILE +# define STATUS_END_OF_FILE ((NTSTATUS) 0xC0000011L) +#endif + +#ifndef STATUS_WRONG_VOLUME +# define STATUS_WRONG_VOLUME ((NTSTATUS) 0xC0000012L) +#endif + +#ifndef STATUS_NO_MEDIA_IN_DEVICE +# define STATUS_NO_MEDIA_IN_DEVICE ((NTSTATUS) 0xC0000013L) +#endif + +#ifndef STATUS_UNRECOGNIZED_MEDIA +# define STATUS_UNRECOGNIZED_MEDIA ((NTSTATUS) 0xC0000014L) +#endif + +#ifndef STATUS_NONEXISTENT_SECTOR +# define STATUS_NONEXISTENT_SECTOR ((NTSTATUS) 0xC0000015L) +#endif + +#ifndef STATUS_MORE_PROCESSING_REQUIRED +# define STATUS_MORE_PROCESSING_REQUIRED ((NTSTATUS) 0xC0000016L) +#endif + +#ifndef STATUS_NO_MEMORY +# define STATUS_NO_MEMORY ((NTSTATUS) 0xC0000017L) +#endif + +#ifndef STATUS_CONFLICTING_ADDRESSES +# define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS) 0xC0000018L) +#endif + +#ifndef STATUS_NOT_MAPPED_VIEW +# define STATUS_NOT_MAPPED_VIEW ((NTSTATUS) 0xC0000019L) +#endif + +#ifndef STATUS_UNABLE_TO_FREE_VM +# define STATUS_UNABLE_TO_FREE_VM ((NTSTATUS) 0xC000001AL) +#endif + +#ifndef STATUS_UNABLE_TO_DELETE_SECTION +# define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS) 0xC000001BL) +#endif + +#ifndef STATUS_INVALID_SYSTEM_SERVICE +# define STATUS_INVALID_SYSTEM_SERVICE ((NTSTATUS) 0xC000001CL) +#endif + +#ifndef STATUS_ILLEGAL_INSTRUCTION +# define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS) 0xC000001DL) +#endif + +#ifndef STATUS_INVALID_LOCK_SEQUENCE +# define STATUS_INVALID_LOCK_SEQUENCE ((NTSTATUS) 0xC000001EL) +#endif + +#ifndef STATUS_INVALID_VIEW_SIZE +# define STATUS_INVALID_VIEW_SIZE ((NTSTATUS) 0xC000001FL) +#endif + +#ifndef STATUS_INVALID_FILE_FOR_SECTION +# define STATUS_INVALID_FILE_FOR_SECTION ((NTSTATUS) 0xC0000020L) +#endif + +#ifndef STATUS_ALREADY_COMMITTED +# define STATUS_ALREADY_COMMITTED ((NTSTATUS) 0xC0000021L) +#endif + +#ifndef STATUS_ACCESS_DENIED +# define STATUS_ACCESS_DENIED ((NTSTATUS) 0xC0000022L) +#endif + +#ifndef STATUS_BUFFER_TOO_SMALL +# define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xC0000023L) +#endif + +#ifndef STATUS_OBJECT_TYPE_MISMATCH +# define STATUS_OBJECT_TYPE_MISMATCH ((NTSTATUS) 0xC0000024L) +#endif + +#ifndef STATUS_NONCONTINUABLE_EXCEPTION +# define STATUS_NONCONTINUABLE_EXCEPTION ((NTSTATUS) 0xC0000025L) +#endif + +#ifndef STATUS_INVALID_DISPOSITION +# define STATUS_INVALID_DISPOSITION ((NTSTATUS) 0xC0000026L) +#endif + +#ifndef STATUS_UNWIND +# define STATUS_UNWIND ((NTSTATUS) 0xC0000027L) +#endif + +#ifndef STATUS_BAD_STACK +# define STATUS_BAD_STACK ((NTSTATUS) 0xC0000028L) +#endif + +#ifndef STATUS_INVALID_UNWIND_TARGET +# define STATUS_INVALID_UNWIND_TARGET ((NTSTATUS) 0xC0000029L) +#endif + +#ifndef STATUS_NOT_LOCKED +# define STATUS_NOT_LOCKED ((NTSTATUS) 0xC000002AL) +#endif + +#ifndef STATUS_PARITY_ERROR +# define STATUS_PARITY_ERROR ((NTSTATUS) 0xC000002BL) +#endif + +#ifndef STATUS_UNABLE_TO_DECOMMIT_VM +# define STATUS_UNABLE_TO_DECOMMIT_VM ((NTSTATUS) 0xC000002CL) +#endif + +#ifndef STATUS_NOT_COMMITTED +# define STATUS_NOT_COMMITTED ((NTSTATUS) 0xC000002DL) +#endif + +#ifndef STATUS_INVALID_PORT_ATTRIBUTES +# define STATUS_INVALID_PORT_ATTRIBUTES ((NTSTATUS) 0xC000002EL) +#endif + +#ifndef STATUS_PORT_MESSAGE_TOO_LONG +# define STATUS_PORT_MESSAGE_TOO_LONG ((NTSTATUS) 0xC000002FL) +#endif + +#ifndef STATUS_INVALID_PARAMETER_MIX +# define STATUS_INVALID_PARAMETER_MIX ((NTSTATUS) 0xC0000030L) +#endif + +#ifndef STATUS_INVALID_QUOTA_LOWER +# define STATUS_INVALID_QUOTA_LOWER ((NTSTATUS) 0xC0000031L) +#endif + +#ifndef STATUS_DISK_CORRUPT_ERROR +# define STATUS_DISK_CORRUPT_ERROR ((NTSTATUS) 0xC0000032L) +#endif + +#ifndef STATUS_OBJECT_NAME_INVALID +# define STATUS_OBJECT_NAME_INVALID ((NTSTATUS) 0xC0000033L) +#endif + +#ifndef STATUS_OBJECT_NAME_NOT_FOUND +# define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS) 0xC0000034L) +#endif + +#ifndef STATUS_OBJECT_NAME_COLLISION +# define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS) 0xC0000035L) +#endif + +#ifndef STATUS_PORT_DISCONNECTED +# define STATUS_PORT_DISCONNECTED ((NTSTATUS) 0xC0000037L) +#endif + +#ifndef STATUS_DEVICE_ALREADY_ATTACHED +# define STATUS_DEVICE_ALREADY_ATTACHED ((NTSTATUS) 0xC0000038L) +#endif + +#ifndef STATUS_OBJECT_PATH_INVALID +# define STATUS_OBJECT_PATH_INVALID ((NTSTATUS) 0xC0000039L) +#endif + +#ifndef STATUS_OBJECT_PATH_NOT_FOUND +# define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS) 0xC000003AL) +#endif + +#ifndef STATUS_OBJECT_PATH_SYNTAX_BAD +# define STATUS_OBJECT_PATH_SYNTAX_BAD ((NTSTATUS) 0xC000003BL) +#endif + +#ifndef STATUS_DATA_OVERRUN +# define STATUS_DATA_OVERRUN ((NTSTATUS) 0xC000003CL) +#endif + +#ifndef STATUS_DATA_LATE_ERROR +# define STATUS_DATA_LATE_ERROR ((NTSTATUS) 0xC000003DL) +#endif + +#ifndef STATUS_DATA_ERROR +# define STATUS_DATA_ERROR ((NTSTATUS) 0xC000003EL) +#endif + +#ifndef STATUS_CRC_ERROR +# define STATUS_CRC_ERROR ((NTSTATUS) 0xC000003FL) +#endif + +#ifndef STATUS_SECTION_TOO_BIG +# define STATUS_SECTION_TOO_BIG ((NTSTATUS) 0xC0000040L) +#endif + +#ifndef STATUS_PORT_CONNECTION_REFUSED +# define STATUS_PORT_CONNECTION_REFUSED ((NTSTATUS) 0xC0000041L) +#endif + +#ifndef STATUS_INVALID_PORT_HANDLE +# define STATUS_INVALID_PORT_HANDLE ((NTSTATUS) 0xC0000042L) +#endif + +#ifndef STATUS_SHARING_VIOLATION +# define STATUS_SHARING_VIOLATION ((NTSTATUS) 0xC0000043L) +#endif + +#ifndef STATUS_QUOTA_EXCEEDED +# define STATUS_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000044L) +#endif + +#ifndef STATUS_INVALID_PAGE_PROTECTION +# define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS) 0xC0000045L) +#endif + +#ifndef STATUS_MUTANT_NOT_OWNED +# define STATUS_MUTANT_NOT_OWNED ((NTSTATUS) 0xC0000046L) +#endif + +#ifndef STATUS_SEMAPHORE_LIMIT_EXCEEDED +# define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000047L) +#endif + +#ifndef STATUS_PORT_ALREADY_SET +# define STATUS_PORT_ALREADY_SET ((NTSTATUS) 0xC0000048L) +#endif + +#ifndef STATUS_SECTION_NOT_IMAGE +# define STATUS_SECTION_NOT_IMAGE ((NTSTATUS) 0xC0000049L) +#endif + +#ifndef STATUS_SUSPEND_COUNT_EXCEEDED +# define STATUS_SUSPEND_COUNT_EXCEEDED ((NTSTATUS) 0xC000004AL) +#endif + +#ifndef STATUS_THREAD_IS_TERMINATING +# define STATUS_THREAD_IS_TERMINATING ((NTSTATUS) 0xC000004BL) +#endif + +#ifndef STATUS_BAD_WORKING_SET_LIMIT +# define STATUS_BAD_WORKING_SET_LIMIT ((NTSTATUS) 0xC000004CL) +#endif + +#ifndef STATUS_INCOMPATIBLE_FILE_MAP +# define STATUS_INCOMPATIBLE_FILE_MAP ((NTSTATUS) 0xC000004DL) +#endif + +#ifndef STATUS_SECTION_PROTECTION +# define STATUS_SECTION_PROTECTION ((NTSTATUS) 0xC000004EL) +#endif + +#ifndef STATUS_EAS_NOT_SUPPORTED +# define STATUS_EAS_NOT_SUPPORTED ((NTSTATUS) 0xC000004FL) +#endif + +#ifndef STATUS_EA_TOO_LARGE +# define STATUS_EA_TOO_LARGE ((NTSTATUS) 0xC0000050L) +#endif + +#ifndef STATUS_NONEXISTENT_EA_ENTRY +# define STATUS_NONEXISTENT_EA_ENTRY ((NTSTATUS) 0xC0000051L) +#endif + +#ifndef STATUS_NO_EAS_ON_FILE +# define STATUS_NO_EAS_ON_FILE ((NTSTATUS) 0xC0000052L) +#endif + +#ifndef STATUS_EA_CORRUPT_ERROR +# define STATUS_EA_CORRUPT_ERROR ((NTSTATUS) 0xC0000053L) +#endif + +#ifndef STATUS_FILE_LOCK_CONFLICT +# define STATUS_FILE_LOCK_CONFLICT ((NTSTATUS) 0xC0000054L) +#endif + +#ifndef STATUS_LOCK_NOT_GRANTED +# define STATUS_LOCK_NOT_GRANTED ((NTSTATUS) 0xC0000055L) +#endif + +#ifndef STATUS_DELETE_PENDING +# define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056L) +#endif + +#ifndef STATUS_CTL_FILE_NOT_SUPPORTED +# define STATUS_CTL_FILE_NOT_SUPPORTED ((NTSTATUS) 0xC0000057L) +#endif + +#ifndef STATUS_UNKNOWN_REVISION +# define STATUS_UNKNOWN_REVISION ((NTSTATUS) 0xC0000058L) +#endif + +#ifndef STATUS_REVISION_MISMATCH +# define STATUS_REVISION_MISMATCH ((NTSTATUS) 0xC0000059L) +#endif + +#ifndef STATUS_INVALID_OWNER +# define STATUS_INVALID_OWNER ((NTSTATUS) 0xC000005AL) +#endif + +#ifndef STATUS_INVALID_PRIMARY_GROUP +# define STATUS_INVALID_PRIMARY_GROUP ((NTSTATUS) 0xC000005BL) +#endif + +#ifndef STATUS_NO_IMPERSONATION_TOKEN +# define STATUS_NO_IMPERSONATION_TOKEN ((NTSTATUS) 0xC000005CL) +#endif + +#ifndef STATUS_CANT_DISABLE_MANDATORY +# define STATUS_CANT_DISABLE_MANDATORY ((NTSTATUS) 0xC000005DL) +#endif + +#ifndef STATUS_NO_LOGON_SERVERS +# define STATUS_NO_LOGON_SERVERS ((NTSTATUS) 0xC000005EL) +#endif + +#ifndef STATUS_NO_SUCH_LOGON_SESSION +# define STATUS_NO_SUCH_LOGON_SESSION ((NTSTATUS) 0xC000005FL) +#endif + +#ifndef STATUS_NO_SUCH_PRIVILEGE +# define STATUS_NO_SUCH_PRIVILEGE ((NTSTATUS) 0xC0000060L) +#endif + +#ifndef STATUS_PRIVILEGE_NOT_HELD +# define STATUS_PRIVILEGE_NOT_HELD ((NTSTATUS) 0xC0000061L) +#endif + +#ifndef STATUS_INVALID_ACCOUNT_NAME +# define STATUS_INVALID_ACCOUNT_NAME ((NTSTATUS) 0xC0000062L) +#endif + +#ifndef STATUS_USER_EXISTS +# define STATUS_USER_EXISTS ((NTSTATUS) 0xC0000063L) +#endif + +#ifndef STATUS_NO_SUCH_USER +# define STATUS_NO_SUCH_USER ((NTSTATUS) 0xC0000064L) +#endif + +#ifndef STATUS_GROUP_EXISTS +# define STATUS_GROUP_EXISTS ((NTSTATUS) 0xC0000065L) +#endif + +#ifndef STATUS_NO_SUCH_GROUP +# define STATUS_NO_SUCH_GROUP ((NTSTATUS) 0xC0000066L) +#endif + +#ifndef STATUS_MEMBER_IN_GROUP +# define STATUS_MEMBER_IN_GROUP ((NTSTATUS) 0xC0000067L) +#endif + +#ifndef STATUS_MEMBER_NOT_IN_GROUP +# define STATUS_MEMBER_NOT_IN_GROUP ((NTSTATUS) 0xC0000068L) +#endif + +#ifndef STATUS_LAST_ADMIN +# define STATUS_LAST_ADMIN ((NTSTATUS) 0xC0000069L) +#endif + +#ifndef STATUS_WRONG_PASSWORD +# define STATUS_WRONG_PASSWORD ((NTSTATUS) 0xC000006AL) +#endif + +#ifndef STATUS_ILL_FORMED_PASSWORD +# define STATUS_ILL_FORMED_PASSWORD ((NTSTATUS) 0xC000006BL) +#endif + +#ifndef STATUS_PASSWORD_RESTRICTION +# define STATUS_PASSWORD_RESTRICTION ((NTSTATUS) 0xC000006CL) +#endif + +#ifndef STATUS_LOGON_FAILURE +# define STATUS_LOGON_FAILURE ((NTSTATUS) 0xC000006DL) +#endif + +#ifndef STATUS_ACCOUNT_RESTRICTION +# define STATUS_ACCOUNT_RESTRICTION ((NTSTATUS) 0xC000006EL) +#endif + +#ifndef STATUS_INVALID_LOGON_HOURS +# define STATUS_INVALID_LOGON_HOURS ((NTSTATUS) 0xC000006FL) +#endif + +#ifndef STATUS_INVALID_WORKSTATION +# define STATUS_INVALID_WORKSTATION ((NTSTATUS) 0xC0000070L) +#endif + +#ifndef STATUS_PASSWORD_EXPIRED +# define STATUS_PASSWORD_EXPIRED ((NTSTATUS) 0xC0000071L) +#endif + +#ifndef STATUS_ACCOUNT_DISABLED +# define STATUS_ACCOUNT_DISABLED ((NTSTATUS) 0xC0000072L) +#endif + +#ifndef STATUS_NONE_MAPPED +# define STATUS_NONE_MAPPED ((NTSTATUS) 0xC0000073L) +#endif + +#ifndef STATUS_TOO_MANY_LUIDS_REQUESTED +# define STATUS_TOO_MANY_LUIDS_REQUESTED ((NTSTATUS) 0xC0000074L) +#endif + +#ifndef STATUS_LUIDS_EXHAUSTED +# define STATUS_LUIDS_EXHAUSTED ((NTSTATUS) 0xC0000075L) +#endif + +#ifndef STATUS_INVALID_SUB_AUTHORITY +# define STATUS_INVALID_SUB_AUTHORITY ((NTSTATUS) 0xC0000076L) +#endif + +#ifndef STATUS_INVALID_ACL +# define STATUS_INVALID_ACL ((NTSTATUS) 0xC0000077L) +#endif + +#ifndef STATUS_INVALID_SID +# define STATUS_INVALID_SID ((NTSTATUS) 0xC0000078L) +#endif + +#ifndef STATUS_INVALID_SECURITY_DESCR +# define STATUS_INVALID_SECURITY_DESCR ((NTSTATUS) 0xC0000079L) +#endif + +#ifndef STATUS_PROCEDURE_NOT_FOUND +# define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS) 0xC000007AL) +#endif + +#ifndef STATUS_INVALID_IMAGE_FORMAT +# define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS) 0xC000007BL) +#endif + +#ifndef STATUS_NO_TOKEN +# define STATUS_NO_TOKEN ((NTSTATUS) 0xC000007CL) +#endif + +#ifndef STATUS_BAD_INHERITANCE_ACL +# define STATUS_BAD_INHERITANCE_ACL ((NTSTATUS) 0xC000007DL) +#endif + +#ifndef STATUS_RANGE_NOT_LOCKED +# define STATUS_RANGE_NOT_LOCKED ((NTSTATUS) 0xC000007EL) +#endif + +#ifndef STATUS_DISK_FULL +# define STATUS_DISK_FULL ((NTSTATUS) 0xC000007FL) +#endif + +#ifndef STATUS_SERVER_DISABLED +# define STATUS_SERVER_DISABLED ((NTSTATUS) 0xC0000080L) +#endif + +#ifndef STATUS_SERVER_NOT_DISABLED +# define STATUS_SERVER_NOT_DISABLED ((NTSTATUS) 0xC0000081L) +#endif + +#ifndef STATUS_TOO_MANY_GUIDS_REQUESTED +# define STATUS_TOO_MANY_GUIDS_REQUESTED ((NTSTATUS) 0xC0000082L) +#endif + +#ifndef STATUS_GUIDS_EXHAUSTED +# define STATUS_GUIDS_EXHAUSTED ((NTSTATUS) 0xC0000083L) +#endif + +#ifndef STATUS_INVALID_ID_AUTHORITY +# define STATUS_INVALID_ID_AUTHORITY ((NTSTATUS) 0xC0000084L) +#endif + +#ifndef STATUS_AGENTS_EXHAUSTED +# define STATUS_AGENTS_EXHAUSTED ((NTSTATUS) 0xC0000085L) +#endif + +#ifndef STATUS_INVALID_VOLUME_LABEL +# define STATUS_INVALID_VOLUME_LABEL ((NTSTATUS) 0xC0000086L) +#endif + +#ifndef STATUS_SECTION_NOT_EXTENDED +# define STATUS_SECTION_NOT_EXTENDED ((NTSTATUS) 0xC0000087L) +#endif + +#ifndef STATUS_NOT_MAPPED_DATA +# define STATUS_NOT_MAPPED_DATA ((NTSTATUS) 0xC0000088L) +#endif + +#ifndef STATUS_RESOURCE_DATA_NOT_FOUND +# define STATUS_RESOURCE_DATA_NOT_FOUND ((NTSTATUS) 0xC0000089L) +#endif + +#ifndef STATUS_RESOURCE_TYPE_NOT_FOUND +# define STATUS_RESOURCE_TYPE_NOT_FOUND ((NTSTATUS) 0xC000008AL) +#endif + +#ifndef STATUS_RESOURCE_NAME_NOT_FOUND +# define STATUS_RESOURCE_NAME_NOT_FOUND ((NTSTATUS) 0xC000008BL) +#endif + +#ifndef STATUS_ARRAY_BOUNDS_EXCEEDED +# define STATUS_ARRAY_BOUNDS_EXCEEDED ((NTSTATUS) 0xC000008CL) +#endif + +#ifndef STATUS_FLOAT_DENORMAL_OPERAND +# define STATUS_FLOAT_DENORMAL_OPERAND ((NTSTATUS) 0xC000008DL) +#endif + +#ifndef STATUS_FLOAT_DIVIDE_BY_ZERO +# define STATUS_FLOAT_DIVIDE_BY_ZERO ((NTSTATUS) 0xC000008EL) +#endif + +#ifndef STATUS_FLOAT_INEXACT_RESULT +# define STATUS_FLOAT_INEXACT_RESULT ((NTSTATUS) 0xC000008FL) +#endif + +#ifndef STATUS_FLOAT_INVALID_OPERATION +# define STATUS_FLOAT_INVALID_OPERATION ((NTSTATUS) 0xC0000090L) +#endif + +#ifndef STATUS_FLOAT_OVERFLOW +# define STATUS_FLOAT_OVERFLOW ((NTSTATUS) 0xC0000091L) +#endif + +#ifndef STATUS_FLOAT_STACK_CHECK +# define STATUS_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000092L) +#endif + +#ifndef STATUS_FLOAT_UNDERFLOW +# define STATUS_FLOAT_UNDERFLOW ((NTSTATUS) 0xC0000093L) +#endif + +#ifndef STATUS_INTEGER_DIVIDE_BY_ZERO +# define STATUS_INTEGER_DIVIDE_BY_ZERO ((NTSTATUS) 0xC0000094L) +#endif + +#ifndef STATUS_INTEGER_OVERFLOW +# define STATUS_INTEGER_OVERFLOW ((NTSTATUS) 0xC0000095L) +#endif + +#ifndef STATUS_PRIVILEGED_INSTRUCTION +# define STATUS_PRIVILEGED_INSTRUCTION ((NTSTATUS) 0xC0000096L) +#endif + +#ifndef STATUS_TOO_MANY_PAGING_FILES +# define STATUS_TOO_MANY_PAGING_FILES ((NTSTATUS) 0xC0000097L) +#endif + +#ifndef STATUS_FILE_INVALID +# define STATUS_FILE_INVALID ((NTSTATUS) 0xC0000098L) +#endif + +#ifndef STATUS_ALLOTTED_SPACE_EXCEEDED +# define STATUS_ALLOTTED_SPACE_EXCEEDED ((NTSTATUS) 0xC0000099L) +#endif + +#ifndef STATUS_INSUFFICIENT_RESOURCES +# define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS) 0xC000009AL) +#endif + +#ifndef STATUS_DFS_EXIT_PATH_FOUND +# define STATUS_DFS_EXIT_PATH_FOUND ((NTSTATUS) 0xC000009BL) +#endif + +#ifndef STATUS_DEVICE_DATA_ERROR +# define STATUS_DEVICE_DATA_ERROR ((NTSTATUS) 0xC000009CL) +#endif + +#ifndef STATUS_DEVICE_NOT_CONNECTED +# define STATUS_DEVICE_NOT_CONNECTED ((NTSTATUS) 0xC000009DL) +#endif + +#ifndef STATUS_DEVICE_POWER_FAILURE +# define STATUS_DEVICE_POWER_FAILURE ((NTSTATUS) 0xC000009EL) +#endif + +#ifndef STATUS_FREE_VM_NOT_AT_BASE +# define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS) 0xC000009FL) +#endif + +#ifndef STATUS_MEMORY_NOT_ALLOCATED +# define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS) 0xC00000A0L) +#endif + +#ifndef STATUS_WORKING_SET_QUOTA +# define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xC00000A1L) +#endif + +#ifndef STATUS_MEDIA_WRITE_PROTECTED +# define STATUS_MEDIA_WRITE_PROTECTED ((NTSTATUS) 0xC00000A2L) +#endif + +#ifndef STATUS_DEVICE_NOT_READY +# define STATUS_DEVICE_NOT_READY ((NTSTATUS) 0xC00000A3L) +#endif + +#ifndef STATUS_INVALID_GROUP_ATTRIBUTES +# define STATUS_INVALID_GROUP_ATTRIBUTES ((NTSTATUS) 0xC00000A4L) +#endif + +#ifndef STATUS_BAD_IMPERSONATION_LEVEL +# define STATUS_BAD_IMPERSONATION_LEVEL ((NTSTATUS) 0xC00000A5L) +#endif + +#ifndef STATUS_CANT_OPEN_ANONYMOUS +# define STATUS_CANT_OPEN_ANONYMOUS ((NTSTATUS) 0xC00000A6L) +#endif + +#ifndef STATUS_BAD_VALIDATION_CLASS +# define STATUS_BAD_VALIDATION_CLASS ((NTSTATUS) 0xC00000A7L) +#endif + +#ifndef STATUS_BAD_TOKEN_TYPE +# define STATUS_BAD_TOKEN_TYPE ((NTSTATUS) 0xC00000A8L) +#endif + +#ifndef STATUS_BAD_MASTER_BOOT_RECORD +# define STATUS_BAD_MASTER_BOOT_RECORD ((NTSTATUS) 0xC00000A9L) +#endif + +#ifndef STATUS_INSTRUCTION_MISALIGNMENT +# define STATUS_INSTRUCTION_MISALIGNMENT ((NTSTATUS) 0xC00000AAL) +#endif + +#ifndef STATUS_INSTANCE_NOT_AVAILABLE +# define STATUS_INSTANCE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ABL) +#endif + +#ifndef STATUS_PIPE_NOT_AVAILABLE +# define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ACL) +#endif + +#ifndef STATUS_INVALID_PIPE_STATE +# define STATUS_INVALID_PIPE_STATE ((NTSTATUS) 0xC00000ADL) +#endif + +#ifndef STATUS_PIPE_BUSY +# define STATUS_PIPE_BUSY ((NTSTATUS) 0xC00000AEL) +#endif + +#ifndef STATUS_ILLEGAL_FUNCTION +# define STATUS_ILLEGAL_FUNCTION ((NTSTATUS) 0xC00000AFL) +#endif + +#ifndef STATUS_PIPE_DISCONNECTED +# define STATUS_PIPE_DISCONNECTED ((NTSTATUS) 0xC00000B0L) +#endif + +#ifndef STATUS_PIPE_CLOSING +# define STATUS_PIPE_CLOSING ((NTSTATUS) 0xC00000B1L) +#endif + +#ifndef STATUS_PIPE_CONNECTED +# define STATUS_PIPE_CONNECTED ((NTSTATUS) 0xC00000B2L) +#endif + +#ifndef STATUS_PIPE_LISTENING +# define STATUS_PIPE_LISTENING ((NTSTATUS) 0xC00000B3L) +#endif + +#ifndef STATUS_INVALID_READ_MODE +# define STATUS_INVALID_READ_MODE ((NTSTATUS) 0xC00000B4L) +#endif + +#ifndef STATUS_IO_TIMEOUT +# define STATUS_IO_TIMEOUT ((NTSTATUS) 0xC00000B5L) +#endif + +#ifndef STATUS_FILE_FORCED_CLOSED +# define STATUS_FILE_FORCED_CLOSED ((NTSTATUS) 0xC00000B6L) +#endif + +#ifndef STATUS_PROFILING_NOT_STARTED +# define STATUS_PROFILING_NOT_STARTED ((NTSTATUS) 0xC00000B7L) +#endif + +#ifndef STATUS_PROFILING_NOT_STOPPED +# define STATUS_PROFILING_NOT_STOPPED ((NTSTATUS) 0xC00000B8L) +#endif + +#ifndef STATUS_COULD_NOT_INTERPRET +# define STATUS_COULD_NOT_INTERPRET ((NTSTATUS) 0xC00000B9L) +#endif + +#ifndef STATUS_FILE_IS_A_DIRECTORY +# define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS) 0xC00000BAL) +#endif + +#ifndef STATUS_NOT_SUPPORTED +# define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xC00000BBL) +#endif + +#ifndef STATUS_REMOTE_NOT_LISTENING +# define STATUS_REMOTE_NOT_LISTENING ((NTSTATUS) 0xC00000BCL) +#endif + +#ifndef STATUS_DUPLICATE_NAME +# define STATUS_DUPLICATE_NAME ((NTSTATUS) 0xC00000BDL) +#endif + +#ifndef STATUS_BAD_NETWORK_PATH +# define STATUS_BAD_NETWORK_PATH ((NTSTATUS) 0xC00000BEL) +#endif + +#ifndef STATUS_NETWORK_BUSY +# define STATUS_NETWORK_BUSY ((NTSTATUS) 0xC00000BFL) +#endif + +#ifndef STATUS_DEVICE_DOES_NOT_EXIST +# define STATUS_DEVICE_DOES_NOT_EXIST ((NTSTATUS) 0xC00000C0L) +#endif + +#ifndef STATUS_TOO_MANY_COMMANDS +# define STATUS_TOO_MANY_COMMANDS ((NTSTATUS) 0xC00000C1L) +#endif + +#ifndef STATUS_ADAPTER_HARDWARE_ERROR +# define STATUS_ADAPTER_HARDWARE_ERROR ((NTSTATUS) 0xC00000C2L) +#endif + +#ifndef STATUS_INVALID_NETWORK_RESPONSE +# define STATUS_INVALID_NETWORK_RESPONSE ((NTSTATUS) 0xC00000C3L) +#endif + +#ifndef STATUS_UNEXPECTED_NETWORK_ERROR +# define STATUS_UNEXPECTED_NETWORK_ERROR ((NTSTATUS) 0xC00000C4L) +#endif + +#ifndef STATUS_BAD_REMOTE_ADAPTER +# define STATUS_BAD_REMOTE_ADAPTER ((NTSTATUS) 0xC00000C5L) +#endif + +#ifndef STATUS_PRINT_QUEUE_FULL +# define STATUS_PRINT_QUEUE_FULL ((NTSTATUS) 0xC00000C6L) +#endif + +#ifndef STATUS_NO_SPOOL_SPACE +# define STATUS_NO_SPOOL_SPACE ((NTSTATUS) 0xC00000C7L) +#endif + +#ifndef STATUS_PRINT_CANCELLED +# define STATUS_PRINT_CANCELLED ((NTSTATUS) 0xC00000C8L) +#endif + +#ifndef STATUS_NETWORK_NAME_DELETED +# define STATUS_NETWORK_NAME_DELETED ((NTSTATUS) 0xC00000C9L) +#endif + +#ifndef STATUS_NETWORK_ACCESS_DENIED +# define STATUS_NETWORK_ACCESS_DENIED ((NTSTATUS) 0xC00000CAL) +#endif + +#ifndef STATUS_BAD_DEVICE_TYPE +# define STATUS_BAD_DEVICE_TYPE ((NTSTATUS) 0xC00000CBL) +#endif + +#ifndef STATUS_BAD_NETWORK_NAME +# define STATUS_BAD_NETWORK_NAME ((NTSTATUS) 0xC00000CCL) +#endif + +#ifndef STATUS_TOO_MANY_NAMES +# define STATUS_TOO_MANY_NAMES ((NTSTATUS) 0xC00000CDL) +#endif + +#ifndef STATUS_TOO_MANY_SESSIONS +# define STATUS_TOO_MANY_SESSIONS ((NTSTATUS) 0xC00000CEL) +#endif + +#ifndef STATUS_SHARING_PAUSED +# define STATUS_SHARING_PAUSED ((NTSTATUS) 0xC00000CFL) +#endif + +#ifndef STATUS_REQUEST_NOT_ACCEPTED +# define STATUS_REQUEST_NOT_ACCEPTED ((NTSTATUS) 0xC00000D0L) +#endif + +#ifndef STATUS_REDIRECTOR_PAUSED +# define STATUS_REDIRECTOR_PAUSED ((NTSTATUS) 0xC00000D1L) +#endif + +#ifndef STATUS_NET_WRITE_FAULT +# define STATUS_NET_WRITE_FAULT ((NTSTATUS) 0xC00000D2L) +#endif + +#ifndef STATUS_PROFILING_AT_LIMIT +# define STATUS_PROFILING_AT_LIMIT ((NTSTATUS) 0xC00000D3L) +#endif + +#ifndef STATUS_NOT_SAME_DEVICE +# define STATUS_NOT_SAME_DEVICE ((NTSTATUS) 0xC00000D4L) +#endif + +#ifndef STATUS_FILE_RENAMED +# define STATUS_FILE_RENAMED ((NTSTATUS) 0xC00000D5L) +#endif + +#ifndef STATUS_VIRTUAL_CIRCUIT_CLOSED +# define STATUS_VIRTUAL_CIRCUIT_CLOSED ((NTSTATUS) 0xC00000D6L) +#endif + +#ifndef STATUS_NO_SECURITY_ON_OBJECT +# define STATUS_NO_SECURITY_ON_OBJECT ((NTSTATUS) 0xC00000D7L) +#endif + +#ifndef STATUS_CANT_WAIT +# define STATUS_CANT_WAIT ((NTSTATUS) 0xC00000D8L) +#endif + +#ifndef STATUS_PIPE_EMPTY +# define STATUS_PIPE_EMPTY ((NTSTATUS) 0xC00000D9L) +#endif + +#ifndef STATUS_CANT_ACCESS_DOMAIN_INFO +# define STATUS_CANT_ACCESS_DOMAIN_INFO ((NTSTATUS) 0xC00000DAL) +#endif + +#ifndef STATUS_CANT_TERMINATE_SELF +# define STATUS_CANT_TERMINATE_SELF ((NTSTATUS) 0xC00000DBL) +#endif + +#ifndef STATUS_INVALID_SERVER_STATE +# define STATUS_INVALID_SERVER_STATE ((NTSTATUS) 0xC00000DCL) +#endif + +#ifndef STATUS_INVALID_DOMAIN_STATE +# define STATUS_INVALID_DOMAIN_STATE ((NTSTATUS) 0xC00000DDL) +#endif + +#ifndef STATUS_INVALID_DOMAIN_ROLE +# define STATUS_INVALID_DOMAIN_ROLE ((NTSTATUS) 0xC00000DEL) +#endif + +#ifndef STATUS_NO_SUCH_DOMAIN +# define STATUS_NO_SUCH_DOMAIN ((NTSTATUS) 0xC00000DFL) +#endif + +#ifndef STATUS_DOMAIN_EXISTS +# define STATUS_DOMAIN_EXISTS ((NTSTATUS) 0xC00000E0L) +#endif + +#ifndef STATUS_DOMAIN_LIMIT_EXCEEDED +# define STATUS_DOMAIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00000E1L) +#endif + +#ifndef STATUS_OPLOCK_NOT_GRANTED +# define STATUS_OPLOCK_NOT_GRANTED ((NTSTATUS) 0xC00000E2L) +#endif + +#ifndef STATUS_INVALID_OPLOCK_PROTOCOL +# define STATUS_INVALID_OPLOCK_PROTOCOL ((NTSTATUS) 0xC00000E3L) +#endif + +#ifndef STATUS_INTERNAL_DB_CORRUPTION +# define STATUS_INTERNAL_DB_CORRUPTION ((NTSTATUS) 0xC00000E4L) +#endif + +#ifndef STATUS_INTERNAL_ERROR +# define STATUS_INTERNAL_ERROR ((NTSTATUS) 0xC00000E5L) +#endif + +#ifndef STATUS_GENERIC_NOT_MAPPED +# define STATUS_GENERIC_NOT_MAPPED ((NTSTATUS) 0xC00000E6L) +#endif + +#ifndef STATUS_BAD_DESCRIPTOR_FORMAT +# define STATUS_BAD_DESCRIPTOR_FORMAT ((NTSTATUS) 0xC00000E7L) +#endif + +#ifndef STATUS_INVALID_USER_BUFFER +# define STATUS_INVALID_USER_BUFFER ((NTSTATUS) 0xC00000E8L) +#endif + +#ifndef STATUS_UNEXPECTED_IO_ERROR +# define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS) 0xC00000E9L) +#endif + +#ifndef STATUS_UNEXPECTED_MM_CREATE_ERR +# define STATUS_UNEXPECTED_MM_CREATE_ERR ((NTSTATUS) 0xC00000EAL) +#endif + +#ifndef STATUS_UNEXPECTED_MM_MAP_ERROR +# define STATUS_UNEXPECTED_MM_MAP_ERROR ((NTSTATUS) 0xC00000EBL) +#endif + +#ifndef STATUS_UNEXPECTED_MM_EXTEND_ERR +# define STATUS_UNEXPECTED_MM_EXTEND_ERR ((NTSTATUS) 0xC00000ECL) +#endif + +#ifndef STATUS_NOT_LOGON_PROCESS +# define STATUS_NOT_LOGON_PROCESS ((NTSTATUS) 0xC00000EDL) +#endif + +#ifndef STATUS_LOGON_SESSION_EXISTS +# define STATUS_LOGON_SESSION_EXISTS ((NTSTATUS) 0xC00000EEL) +#endif + +#ifndef STATUS_INVALID_PARAMETER_1 +# define STATUS_INVALID_PARAMETER_1 ((NTSTATUS) 0xC00000EFL) +#endif + +#ifndef STATUS_INVALID_PARAMETER_2 +# define STATUS_INVALID_PARAMETER_2 ((NTSTATUS) 0xC00000F0L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_3 +# define STATUS_INVALID_PARAMETER_3 ((NTSTATUS) 0xC00000F1L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_4 +# define STATUS_INVALID_PARAMETER_4 ((NTSTATUS) 0xC00000F2L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_5 +# define STATUS_INVALID_PARAMETER_5 ((NTSTATUS) 0xC00000F3L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_6 +# define STATUS_INVALID_PARAMETER_6 ((NTSTATUS) 0xC00000F4L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_7 +# define STATUS_INVALID_PARAMETER_7 ((NTSTATUS) 0xC00000F5L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_8 +# define STATUS_INVALID_PARAMETER_8 ((NTSTATUS) 0xC00000F6L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_9 +# define STATUS_INVALID_PARAMETER_9 ((NTSTATUS) 0xC00000F7L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_10 +# define STATUS_INVALID_PARAMETER_10 ((NTSTATUS) 0xC00000F8L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_11 +# define STATUS_INVALID_PARAMETER_11 ((NTSTATUS) 0xC00000F9L) +#endif + +#ifndef STATUS_INVALID_PARAMETER_12 +# define STATUS_INVALID_PARAMETER_12 ((NTSTATUS) 0xC00000FAL) +#endif + +#ifndef STATUS_REDIRECTOR_NOT_STARTED +# define STATUS_REDIRECTOR_NOT_STARTED ((NTSTATUS) 0xC00000FBL) +#endif + +#ifndef STATUS_REDIRECTOR_STARTED +# define STATUS_REDIRECTOR_STARTED ((NTSTATUS) 0xC00000FCL) +#endif + +#ifndef STATUS_STACK_OVERFLOW +# define STATUS_STACK_OVERFLOW ((NTSTATUS) 0xC00000FDL) +#endif + +#ifndef STATUS_NO_SUCH_PACKAGE +# define STATUS_NO_SUCH_PACKAGE ((NTSTATUS) 0xC00000FEL) +#endif + +#ifndef STATUS_BAD_FUNCTION_TABLE +# define STATUS_BAD_FUNCTION_TABLE ((NTSTATUS) 0xC00000FFL) +#endif + +#ifndef STATUS_VARIABLE_NOT_FOUND +# define STATUS_VARIABLE_NOT_FOUND ((NTSTATUS) 0xC0000100L) +#endif + +#ifndef STATUS_DIRECTORY_NOT_EMPTY +# define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xC0000101L) +#endif + +#ifndef STATUS_FILE_CORRUPT_ERROR +# define STATUS_FILE_CORRUPT_ERROR ((NTSTATUS) 0xC0000102L) +#endif + +#ifndef STATUS_NOT_A_DIRECTORY +# define STATUS_NOT_A_DIRECTORY ((NTSTATUS) 0xC0000103L) +#endif + +#ifndef STATUS_BAD_LOGON_SESSION_STATE +# define STATUS_BAD_LOGON_SESSION_STATE ((NTSTATUS) 0xC0000104L) +#endif + +#ifndef STATUS_LOGON_SESSION_COLLISION +# define STATUS_LOGON_SESSION_COLLISION ((NTSTATUS) 0xC0000105L) +#endif + +#ifndef STATUS_NAME_TOO_LONG +# define STATUS_NAME_TOO_LONG ((NTSTATUS) 0xC0000106L) +#endif + +#ifndef STATUS_FILES_OPEN +# define STATUS_FILES_OPEN ((NTSTATUS) 0xC0000107L) +#endif + +#ifndef STATUS_CONNECTION_IN_USE +# define STATUS_CONNECTION_IN_USE ((NTSTATUS) 0xC0000108L) +#endif + +#ifndef STATUS_MESSAGE_NOT_FOUND +# define STATUS_MESSAGE_NOT_FOUND ((NTSTATUS) 0xC0000109L) +#endif + +#ifndef STATUS_PROCESS_IS_TERMINATING +# define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS) 0xC000010AL) +#endif + +#ifndef STATUS_INVALID_LOGON_TYPE +# define STATUS_INVALID_LOGON_TYPE ((NTSTATUS) 0xC000010BL) +#endif + +#ifndef STATUS_NO_GUID_TRANSLATION +# define STATUS_NO_GUID_TRANSLATION ((NTSTATUS) 0xC000010CL) +#endif + +#ifndef STATUS_CANNOT_IMPERSONATE +# define STATUS_CANNOT_IMPERSONATE ((NTSTATUS) 0xC000010DL) +#endif + +#ifndef STATUS_IMAGE_ALREADY_LOADED +# define STATUS_IMAGE_ALREADY_LOADED ((NTSTATUS) 0xC000010EL) +#endif + +#ifndef STATUS_ABIOS_NOT_PRESENT +# define STATUS_ABIOS_NOT_PRESENT ((NTSTATUS) 0xC000010FL) +#endif + +#ifndef STATUS_ABIOS_LID_NOT_EXIST +# define STATUS_ABIOS_LID_NOT_EXIST ((NTSTATUS) 0xC0000110L) +#endif + +#ifndef STATUS_ABIOS_LID_ALREADY_OWNED +# define STATUS_ABIOS_LID_ALREADY_OWNED ((NTSTATUS) 0xC0000111L) +#endif + +#ifndef STATUS_ABIOS_NOT_LID_OWNER +# define STATUS_ABIOS_NOT_LID_OWNER ((NTSTATUS) 0xC0000112L) +#endif + +#ifndef STATUS_ABIOS_INVALID_COMMAND +# define STATUS_ABIOS_INVALID_COMMAND ((NTSTATUS) 0xC0000113L) +#endif + +#ifndef STATUS_ABIOS_INVALID_LID +# define STATUS_ABIOS_INVALID_LID ((NTSTATUS) 0xC0000114L) +#endif + +#ifndef STATUS_ABIOS_SELECTOR_NOT_AVAILABLE +# define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE ((NTSTATUS) 0xC0000115L) +#endif + +#ifndef STATUS_ABIOS_INVALID_SELECTOR +# define STATUS_ABIOS_INVALID_SELECTOR ((NTSTATUS) 0xC0000116L) +#endif + +#ifndef STATUS_NO_LDT +# define STATUS_NO_LDT ((NTSTATUS) 0xC0000117L) +#endif + +#ifndef STATUS_INVALID_LDT_SIZE +# define STATUS_INVALID_LDT_SIZE ((NTSTATUS) 0xC0000118L) +#endif + +#ifndef STATUS_INVALID_LDT_OFFSET +# define STATUS_INVALID_LDT_OFFSET ((NTSTATUS) 0xC0000119L) +#endif + +#ifndef STATUS_INVALID_LDT_DESCRIPTOR +# define STATUS_INVALID_LDT_DESCRIPTOR ((NTSTATUS) 0xC000011AL) +#endif + +#ifndef STATUS_INVALID_IMAGE_NE_FORMAT +# define STATUS_INVALID_IMAGE_NE_FORMAT ((NTSTATUS) 0xC000011BL) +#endif + +#ifndef STATUS_RXACT_INVALID_STATE +# define STATUS_RXACT_INVALID_STATE ((NTSTATUS) 0xC000011CL) +#endif + +#ifndef STATUS_RXACT_COMMIT_FAILURE +# define STATUS_RXACT_COMMIT_FAILURE ((NTSTATUS) 0xC000011DL) +#endif + +#ifndef STATUS_MAPPED_FILE_SIZE_ZERO +# define STATUS_MAPPED_FILE_SIZE_ZERO ((NTSTATUS) 0xC000011EL) +#endif + +#ifndef STATUS_TOO_MANY_OPENED_FILES +# define STATUS_TOO_MANY_OPENED_FILES ((NTSTATUS) 0xC000011FL) +#endif + +#ifndef STATUS_CANCELLED +# define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) +#endif + +#ifndef STATUS_CANNOT_DELETE +# define STATUS_CANNOT_DELETE ((NTSTATUS) 0xC0000121L) +#endif + +#ifndef STATUS_INVALID_COMPUTER_NAME +# define STATUS_INVALID_COMPUTER_NAME ((NTSTATUS) 0xC0000122L) +#endif + +#ifndef STATUS_FILE_DELETED +# define STATUS_FILE_DELETED ((NTSTATUS) 0xC0000123L) +#endif + +#ifndef STATUS_SPECIAL_ACCOUNT +# define STATUS_SPECIAL_ACCOUNT ((NTSTATUS) 0xC0000124L) +#endif + +#ifndef STATUS_SPECIAL_GROUP +# define STATUS_SPECIAL_GROUP ((NTSTATUS) 0xC0000125L) +#endif + +#ifndef STATUS_SPECIAL_USER +# define STATUS_SPECIAL_USER ((NTSTATUS) 0xC0000126L) +#endif + +#ifndef STATUS_MEMBERS_PRIMARY_GROUP +# define STATUS_MEMBERS_PRIMARY_GROUP ((NTSTATUS) 0xC0000127L) +#endif + +#ifndef STATUS_FILE_CLOSED +# define STATUS_FILE_CLOSED ((NTSTATUS) 0xC0000128L) +#endif + +#ifndef STATUS_TOO_MANY_THREADS +# define STATUS_TOO_MANY_THREADS ((NTSTATUS) 0xC0000129L) +#endif + +#ifndef STATUS_THREAD_NOT_IN_PROCESS +# define STATUS_THREAD_NOT_IN_PROCESS ((NTSTATUS) 0xC000012AL) +#endif + +#ifndef STATUS_TOKEN_ALREADY_IN_USE +# define STATUS_TOKEN_ALREADY_IN_USE ((NTSTATUS) 0xC000012BL) +#endif + +#ifndef STATUS_PAGEFILE_QUOTA_EXCEEDED +# define STATUS_PAGEFILE_QUOTA_EXCEEDED ((NTSTATUS) 0xC000012CL) +#endif + +#ifndef STATUS_COMMITMENT_LIMIT +# define STATUS_COMMITMENT_LIMIT ((NTSTATUS) 0xC000012DL) +#endif + +#ifndef STATUS_INVALID_IMAGE_LE_FORMAT +# define STATUS_INVALID_IMAGE_LE_FORMAT ((NTSTATUS) 0xC000012EL) +#endif + +#ifndef STATUS_INVALID_IMAGE_NOT_MZ +# define STATUS_INVALID_IMAGE_NOT_MZ ((NTSTATUS) 0xC000012FL) +#endif + +#ifndef STATUS_INVALID_IMAGE_PROTECT +# define STATUS_INVALID_IMAGE_PROTECT ((NTSTATUS) 0xC0000130L) +#endif + +#ifndef STATUS_INVALID_IMAGE_WIN_16 +# define STATUS_INVALID_IMAGE_WIN_16 ((NTSTATUS) 0xC0000131L) +#endif + +#ifndef STATUS_LOGON_SERVER_CONFLICT +# define STATUS_LOGON_SERVER_CONFLICT ((NTSTATUS) 0xC0000132L) +#endif + +#ifndef STATUS_TIME_DIFFERENCE_AT_DC +# define STATUS_TIME_DIFFERENCE_AT_DC ((NTSTATUS) 0xC0000133L) +#endif + +#ifndef STATUS_SYNCHRONIZATION_REQUIRED +# define STATUS_SYNCHRONIZATION_REQUIRED ((NTSTATUS) 0xC0000134L) +#endif + +#ifndef STATUS_DLL_NOT_FOUND +# define STATUS_DLL_NOT_FOUND ((NTSTATUS) 0xC0000135L) +#endif + +#ifndef STATUS_OPEN_FAILED +# define STATUS_OPEN_FAILED ((NTSTATUS) 0xC0000136L) +#endif + +#ifndef STATUS_IO_PRIVILEGE_FAILED +# define STATUS_IO_PRIVILEGE_FAILED ((NTSTATUS) 0xC0000137L) +#endif + +#ifndef STATUS_ORDINAL_NOT_FOUND +# define STATUS_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000138L) +#endif + +#ifndef STATUS_ENTRYPOINT_NOT_FOUND +# define STATUS_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000139L) +#endif + +#ifndef STATUS_CONTROL_C_EXIT +# define STATUS_CONTROL_C_EXIT ((NTSTATUS) 0xC000013AL) +#endif + +#ifndef STATUS_LOCAL_DISCONNECT +# define STATUS_LOCAL_DISCONNECT ((NTSTATUS) 0xC000013BL) +#endif + +#ifndef STATUS_REMOTE_DISCONNECT +# define STATUS_REMOTE_DISCONNECT ((NTSTATUS) 0xC000013CL) +#endif + +#ifndef STATUS_REMOTE_RESOURCES +# define STATUS_REMOTE_RESOURCES ((NTSTATUS) 0xC000013DL) +#endif + +#ifndef STATUS_LINK_FAILED +# define STATUS_LINK_FAILED ((NTSTATUS) 0xC000013EL) +#endif + +#ifndef STATUS_LINK_TIMEOUT +# define STATUS_LINK_TIMEOUT ((NTSTATUS) 0xC000013FL) +#endif + +#ifndef STATUS_INVALID_CONNECTION +# define STATUS_INVALID_CONNECTION ((NTSTATUS) 0xC0000140L) +#endif + +#ifndef STATUS_INVALID_ADDRESS +# define STATUS_INVALID_ADDRESS ((NTSTATUS) 0xC0000141L) +#endif + +#ifndef STATUS_DLL_INIT_FAILED +# define STATUS_DLL_INIT_FAILED ((NTSTATUS) 0xC0000142L) +#endif + +#ifndef STATUS_MISSING_SYSTEMFILE +# define STATUS_MISSING_SYSTEMFILE ((NTSTATUS) 0xC0000143L) +#endif + +#ifndef STATUS_UNHANDLED_EXCEPTION +# define STATUS_UNHANDLED_EXCEPTION ((NTSTATUS) 0xC0000144L) +#endif + +#ifndef STATUS_APP_INIT_FAILURE +# define STATUS_APP_INIT_FAILURE ((NTSTATUS) 0xC0000145L) +#endif + +#ifndef STATUS_PAGEFILE_CREATE_FAILED +# define STATUS_PAGEFILE_CREATE_FAILED ((NTSTATUS) 0xC0000146L) +#endif + +#ifndef STATUS_NO_PAGEFILE +# define STATUS_NO_PAGEFILE ((NTSTATUS) 0xC0000147L) +#endif + +#ifndef STATUS_INVALID_LEVEL +# define STATUS_INVALID_LEVEL ((NTSTATUS) 0xC0000148L) +#endif + +#ifndef STATUS_WRONG_PASSWORD_CORE +# define STATUS_WRONG_PASSWORD_CORE ((NTSTATUS) 0xC0000149L) +#endif + +#ifndef STATUS_ILLEGAL_FLOAT_CONTEXT +# define STATUS_ILLEGAL_FLOAT_CONTEXT ((NTSTATUS) 0xC000014AL) +#endif + +#ifndef STATUS_PIPE_BROKEN +# define STATUS_PIPE_BROKEN ((NTSTATUS) 0xC000014BL) +#endif + +#ifndef STATUS_REGISTRY_CORRUPT +# define STATUS_REGISTRY_CORRUPT ((NTSTATUS) 0xC000014CL) +#endif + +#ifndef STATUS_REGISTRY_IO_FAILED +# define STATUS_REGISTRY_IO_FAILED ((NTSTATUS) 0xC000014DL) +#endif + +#ifndef STATUS_NO_EVENT_PAIR +# define STATUS_NO_EVENT_PAIR ((NTSTATUS) 0xC000014EL) +#endif + +#ifndef STATUS_UNRECOGNIZED_VOLUME +# define STATUS_UNRECOGNIZED_VOLUME ((NTSTATUS) 0xC000014FL) +#endif + +#ifndef STATUS_SERIAL_NO_DEVICE_INITED +# define STATUS_SERIAL_NO_DEVICE_INITED ((NTSTATUS) 0xC0000150L) +#endif + +#ifndef STATUS_NO_SUCH_ALIAS +# define STATUS_NO_SUCH_ALIAS ((NTSTATUS) 0xC0000151L) +#endif + +#ifndef STATUS_MEMBER_NOT_IN_ALIAS +# define STATUS_MEMBER_NOT_IN_ALIAS ((NTSTATUS) 0xC0000152L) +#endif + +#ifndef STATUS_MEMBER_IN_ALIAS +# define STATUS_MEMBER_IN_ALIAS ((NTSTATUS) 0xC0000153L) +#endif + +#ifndef STATUS_ALIAS_EXISTS +# define STATUS_ALIAS_EXISTS ((NTSTATUS) 0xC0000154L) +#endif + +#ifndef STATUS_LOGON_NOT_GRANTED +# define STATUS_LOGON_NOT_GRANTED ((NTSTATUS) 0xC0000155L) +#endif + +#ifndef STATUS_TOO_MANY_SECRETS +# define STATUS_TOO_MANY_SECRETS ((NTSTATUS) 0xC0000156L) +#endif + +#ifndef STATUS_SECRET_TOO_LONG +# define STATUS_SECRET_TOO_LONG ((NTSTATUS) 0xC0000157L) +#endif + +#ifndef STATUS_INTERNAL_DB_ERROR +# define STATUS_INTERNAL_DB_ERROR ((NTSTATUS) 0xC0000158L) +#endif + +#ifndef STATUS_FULLSCREEN_MODE +# define STATUS_FULLSCREEN_MODE ((NTSTATUS) 0xC0000159L) +#endif + +#ifndef STATUS_TOO_MANY_CONTEXT_IDS +# define STATUS_TOO_MANY_CONTEXT_IDS ((NTSTATUS) 0xC000015AL) +#endif + +#ifndef STATUS_LOGON_TYPE_NOT_GRANTED +# define STATUS_LOGON_TYPE_NOT_GRANTED ((NTSTATUS) 0xC000015BL) +#endif + +#ifndef STATUS_NOT_REGISTRY_FILE +# define STATUS_NOT_REGISTRY_FILE ((NTSTATUS) 0xC000015CL) +#endif + +#ifndef STATUS_NT_CROSS_ENCRYPTION_REQUIRED +# define STATUS_NT_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000015DL) +#endif + +#ifndef STATUS_DOMAIN_CTRLR_CONFIG_ERROR +# define STATUS_DOMAIN_CTRLR_CONFIG_ERROR ((NTSTATUS) 0xC000015EL) +#endif + +#ifndef STATUS_FT_MISSING_MEMBER +# define STATUS_FT_MISSING_MEMBER ((NTSTATUS) 0xC000015FL) +#endif + +#ifndef STATUS_ILL_FORMED_SERVICE_ENTRY +# define STATUS_ILL_FORMED_SERVICE_ENTRY ((NTSTATUS) 0xC0000160L) +#endif + +#ifndef STATUS_ILLEGAL_CHARACTER +# define STATUS_ILLEGAL_CHARACTER ((NTSTATUS) 0xC0000161L) +#endif + +#ifndef STATUS_UNMAPPABLE_CHARACTER +# define STATUS_UNMAPPABLE_CHARACTER ((NTSTATUS) 0xC0000162L) +#endif + +#ifndef STATUS_UNDEFINED_CHARACTER +# define STATUS_UNDEFINED_CHARACTER ((NTSTATUS) 0xC0000163L) +#endif + +#ifndef STATUS_FLOPPY_VOLUME +# define STATUS_FLOPPY_VOLUME ((NTSTATUS) 0xC0000164L) +#endif + +#ifndef STATUS_FLOPPY_ID_MARK_NOT_FOUND +# define STATUS_FLOPPY_ID_MARK_NOT_FOUND ((NTSTATUS) 0xC0000165L) +#endif + +#ifndef STATUS_FLOPPY_WRONG_CYLINDER +# define STATUS_FLOPPY_WRONG_CYLINDER ((NTSTATUS) 0xC0000166L) +#endif + +#ifndef STATUS_FLOPPY_UNKNOWN_ERROR +# define STATUS_FLOPPY_UNKNOWN_ERROR ((NTSTATUS) 0xC0000167L) +#endif + +#ifndef STATUS_FLOPPY_BAD_REGISTERS +# define STATUS_FLOPPY_BAD_REGISTERS ((NTSTATUS) 0xC0000168L) +#endif + +#ifndef STATUS_DISK_RECALIBRATE_FAILED +# define STATUS_DISK_RECALIBRATE_FAILED ((NTSTATUS) 0xC0000169L) +#endif + +#ifndef STATUS_DISK_OPERATION_FAILED +# define STATUS_DISK_OPERATION_FAILED ((NTSTATUS) 0xC000016AL) +#endif + +#ifndef STATUS_DISK_RESET_FAILED +# define STATUS_DISK_RESET_FAILED ((NTSTATUS) 0xC000016BL) +#endif + +#ifndef STATUS_SHARED_IRQ_BUSY +# define STATUS_SHARED_IRQ_BUSY ((NTSTATUS) 0xC000016CL) +#endif + +#ifndef STATUS_FT_ORPHANING +# define STATUS_FT_ORPHANING ((NTSTATUS) 0xC000016DL) +#endif + +#ifndef STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT +# define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT ((NTSTATUS) 0xC000016EL) +#endif + +#ifndef STATUS_PARTITION_FAILURE +# define STATUS_PARTITION_FAILURE ((NTSTATUS) 0xC0000172L) +#endif + +#ifndef STATUS_INVALID_BLOCK_LENGTH +# define STATUS_INVALID_BLOCK_LENGTH ((NTSTATUS) 0xC0000173L) +#endif + +#ifndef STATUS_DEVICE_NOT_PARTITIONED +# define STATUS_DEVICE_NOT_PARTITIONED ((NTSTATUS) 0xC0000174L) +#endif + +#ifndef STATUS_UNABLE_TO_LOCK_MEDIA +# define STATUS_UNABLE_TO_LOCK_MEDIA ((NTSTATUS) 0xC0000175L) +#endif + +#ifndef STATUS_UNABLE_TO_UNLOAD_MEDIA +# define STATUS_UNABLE_TO_UNLOAD_MEDIA ((NTSTATUS) 0xC0000176L) +#endif + +#ifndef STATUS_EOM_OVERFLOW +# define STATUS_EOM_OVERFLOW ((NTSTATUS) 0xC0000177L) +#endif + +#ifndef STATUS_NO_MEDIA +# define STATUS_NO_MEDIA ((NTSTATUS) 0xC0000178L) +#endif + +#ifndef STATUS_NO_SUCH_MEMBER +# define STATUS_NO_SUCH_MEMBER ((NTSTATUS) 0xC000017AL) +#endif + +#ifndef STATUS_INVALID_MEMBER +# define STATUS_INVALID_MEMBER ((NTSTATUS) 0xC000017BL) +#endif + +#ifndef STATUS_KEY_DELETED +# define STATUS_KEY_DELETED ((NTSTATUS) 0xC000017CL) +#endif + +#ifndef STATUS_NO_LOG_SPACE +# define STATUS_NO_LOG_SPACE ((NTSTATUS) 0xC000017DL) +#endif + +#ifndef STATUS_TOO_MANY_SIDS +# define STATUS_TOO_MANY_SIDS ((NTSTATUS) 0xC000017EL) +#endif + +#ifndef STATUS_LM_CROSS_ENCRYPTION_REQUIRED +# define STATUS_LM_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000017FL) +#endif + +#ifndef STATUS_KEY_HAS_CHILDREN +# define STATUS_KEY_HAS_CHILDREN ((NTSTATUS) 0xC0000180L) +#endif + +#ifndef STATUS_CHILD_MUST_BE_VOLATILE +# define STATUS_CHILD_MUST_BE_VOLATILE ((NTSTATUS) 0xC0000181L) +#endif + +#ifndef STATUS_DEVICE_CONFIGURATION_ERROR +# define STATUS_DEVICE_CONFIGURATION_ERROR ((NTSTATUS) 0xC0000182L) +#endif + +#ifndef STATUS_DRIVER_INTERNAL_ERROR +# define STATUS_DRIVER_INTERNAL_ERROR ((NTSTATUS) 0xC0000183L) +#endif + +#ifndef STATUS_INVALID_DEVICE_STATE +# define STATUS_INVALID_DEVICE_STATE ((NTSTATUS) 0xC0000184L) +#endif + +#ifndef STATUS_IO_DEVICE_ERROR +# define STATUS_IO_DEVICE_ERROR ((NTSTATUS) 0xC0000185L) +#endif + +#ifndef STATUS_DEVICE_PROTOCOL_ERROR +# define STATUS_DEVICE_PROTOCOL_ERROR ((NTSTATUS) 0xC0000186L) +#endif + +#ifndef STATUS_BACKUP_CONTROLLER +# define STATUS_BACKUP_CONTROLLER ((NTSTATUS) 0xC0000187L) +#endif + +#ifndef STATUS_LOG_FILE_FULL +# define STATUS_LOG_FILE_FULL ((NTSTATUS) 0xC0000188L) +#endif + +#ifndef STATUS_TOO_LATE +# define STATUS_TOO_LATE ((NTSTATUS) 0xC0000189L) +#endif + +#ifndef STATUS_NO_TRUST_LSA_SECRET +# define STATUS_NO_TRUST_LSA_SECRET ((NTSTATUS) 0xC000018AL) +#endif + +#ifndef STATUS_NO_TRUST_SAM_ACCOUNT +# define STATUS_NO_TRUST_SAM_ACCOUNT ((NTSTATUS) 0xC000018BL) +#endif + +#ifndef STATUS_TRUSTED_DOMAIN_FAILURE +# define STATUS_TRUSTED_DOMAIN_FAILURE ((NTSTATUS) 0xC000018CL) +#endif + +#ifndef STATUS_TRUSTED_RELATIONSHIP_FAILURE +# define STATUS_TRUSTED_RELATIONSHIP_FAILURE ((NTSTATUS) 0xC000018DL) +#endif + +#ifndef STATUS_EVENTLOG_FILE_CORRUPT +# define STATUS_EVENTLOG_FILE_CORRUPT ((NTSTATUS) 0xC000018EL) +#endif + +#ifndef STATUS_EVENTLOG_CANT_START +# define STATUS_EVENTLOG_CANT_START ((NTSTATUS) 0xC000018FL) +#endif + +#ifndef STATUS_TRUST_FAILURE +# define STATUS_TRUST_FAILURE ((NTSTATUS) 0xC0000190L) +#endif + +#ifndef STATUS_MUTANT_LIMIT_EXCEEDED +# define STATUS_MUTANT_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000191L) +#endif + +#ifndef STATUS_NETLOGON_NOT_STARTED +# define STATUS_NETLOGON_NOT_STARTED ((NTSTATUS) 0xC0000192L) +#endif + +#ifndef STATUS_ACCOUNT_EXPIRED +# define STATUS_ACCOUNT_EXPIRED ((NTSTATUS) 0xC0000193L) +#endif + +#ifndef STATUS_POSSIBLE_DEADLOCK +# define STATUS_POSSIBLE_DEADLOCK ((NTSTATUS) 0xC0000194L) +#endif + +#ifndef STATUS_NETWORK_CREDENTIAL_CONFLICT +# define STATUS_NETWORK_CREDENTIAL_CONFLICT ((NTSTATUS) 0xC0000195L) +#endif + +#ifndef STATUS_REMOTE_SESSION_LIMIT +# define STATUS_REMOTE_SESSION_LIMIT ((NTSTATUS) 0xC0000196L) +#endif + +#ifndef STATUS_EVENTLOG_FILE_CHANGED +# define STATUS_EVENTLOG_FILE_CHANGED ((NTSTATUS) 0xC0000197L) +#endif + +#ifndef STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT +# define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT ((NTSTATUS) 0xC0000198L) +#endif + +#ifndef STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT +# define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT ((NTSTATUS) 0xC0000199L) +#endif + +#ifndef STATUS_NOLOGON_SERVER_TRUST_ACCOUNT +# define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT ((NTSTATUS) 0xC000019AL) +#endif + +#ifndef STATUS_DOMAIN_TRUST_INCONSISTENT +# define STATUS_DOMAIN_TRUST_INCONSISTENT ((NTSTATUS) 0xC000019BL) +#endif + +#ifndef STATUS_FS_DRIVER_REQUIRED +# define STATUS_FS_DRIVER_REQUIRED ((NTSTATUS) 0xC000019CL) +#endif + +#ifndef STATUS_IMAGE_ALREADY_LOADED_AS_DLL +# define STATUS_IMAGE_ALREADY_LOADED_AS_DLL ((NTSTATUS) 0xC000019DL) +#endif + +#ifndef STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING +# define STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING ((NTSTATUS) 0xC000019EL) +#endif + +#ifndef STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME +# define STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME ((NTSTATUS) 0xC000019FL) +#endif + +#ifndef STATUS_SECURITY_STREAM_IS_INCONSISTENT +# define STATUS_SECURITY_STREAM_IS_INCONSISTENT ((NTSTATUS) 0xC00001A0L) +#endif + +#ifndef STATUS_INVALID_LOCK_RANGE +# define STATUS_INVALID_LOCK_RANGE ((NTSTATUS) 0xC00001A1L) +#endif + +#ifndef STATUS_INVALID_ACE_CONDITION +# define STATUS_INVALID_ACE_CONDITION ((NTSTATUS) 0xC00001A2L) +#endif + +#ifndef STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT +# define STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT ((NTSTATUS) 0xC00001A3L) +#endif + +#ifndef STATUS_NOTIFICATION_GUID_ALREADY_DEFINED +# define STATUS_NOTIFICATION_GUID_ALREADY_DEFINED ((NTSTATUS) 0xC00001A4L) +#endif + +#ifndef STATUS_NETWORK_OPEN_RESTRICTION +# define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS) 0xC0000201L) +#endif + +#ifndef STATUS_NO_USER_SESSION_KEY +# define STATUS_NO_USER_SESSION_KEY ((NTSTATUS) 0xC0000202L) +#endif + +#ifndef STATUS_USER_SESSION_DELETED +# define STATUS_USER_SESSION_DELETED ((NTSTATUS) 0xC0000203L) +#endif + +#ifndef STATUS_RESOURCE_LANG_NOT_FOUND +# define STATUS_RESOURCE_LANG_NOT_FOUND ((NTSTATUS) 0xC0000204L) +#endif + +#ifndef STATUS_INSUFF_SERVER_RESOURCES +# define STATUS_INSUFF_SERVER_RESOURCES ((NTSTATUS) 0xC0000205L) +#endif + +#ifndef STATUS_INVALID_BUFFER_SIZE +# define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS) 0xC0000206L) +#endif + +#ifndef STATUS_INVALID_ADDRESS_COMPONENT +# define STATUS_INVALID_ADDRESS_COMPONENT ((NTSTATUS) 0xC0000207L) +#endif + +#ifndef STATUS_INVALID_ADDRESS_WILDCARD +# define STATUS_INVALID_ADDRESS_WILDCARD ((NTSTATUS) 0xC0000208L) +#endif + +#ifndef STATUS_TOO_MANY_ADDRESSES +# define STATUS_TOO_MANY_ADDRESSES ((NTSTATUS) 0xC0000209L) +#endif + +#ifndef STATUS_ADDRESS_ALREADY_EXISTS +# define STATUS_ADDRESS_ALREADY_EXISTS ((NTSTATUS) 0xC000020AL) +#endif + +#ifndef STATUS_ADDRESS_CLOSED +# define STATUS_ADDRESS_CLOSED ((NTSTATUS) 0xC000020BL) +#endif + +#ifndef STATUS_CONNECTION_DISCONNECTED +# define STATUS_CONNECTION_DISCONNECTED ((NTSTATUS) 0xC000020CL) +#endif + +#ifndef STATUS_CONNECTION_RESET +# define STATUS_CONNECTION_RESET ((NTSTATUS) 0xC000020DL) +#endif + +#ifndef STATUS_TOO_MANY_NODES +# define STATUS_TOO_MANY_NODES ((NTSTATUS) 0xC000020EL) +#endif + +#ifndef STATUS_TRANSACTION_ABORTED +# define STATUS_TRANSACTION_ABORTED ((NTSTATUS) 0xC000020FL) +#endif + +#ifndef STATUS_TRANSACTION_TIMED_OUT +# define STATUS_TRANSACTION_TIMED_OUT ((NTSTATUS) 0xC0000210L) +#endif + +#ifndef STATUS_TRANSACTION_NO_RELEASE +# define STATUS_TRANSACTION_NO_RELEASE ((NTSTATUS) 0xC0000211L) +#endif + +#ifndef STATUS_TRANSACTION_NO_MATCH +# define STATUS_TRANSACTION_NO_MATCH ((NTSTATUS) 0xC0000212L) +#endif + +#ifndef STATUS_TRANSACTION_RESPONDED +# define STATUS_TRANSACTION_RESPONDED ((NTSTATUS) 0xC0000213L) +#endif + +#ifndef STATUS_TRANSACTION_INVALID_ID +# define STATUS_TRANSACTION_INVALID_ID ((NTSTATUS) 0xC0000214L) +#endif + +#ifndef STATUS_TRANSACTION_INVALID_TYPE +# define STATUS_TRANSACTION_INVALID_TYPE ((NTSTATUS) 0xC0000215L) +#endif + +#ifndef STATUS_NOT_SERVER_SESSION +# define STATUS_NOT_SERVER_SESSION ((NTSTATUS) 0xC0000216L) +#endif + +#ifndef STATUS_NOT_CLIENT_SESSION +# define STATUS_NOT_CLIENT_SESSION ((NTSTATUS) 0xC0000217L) +#endif + +#ifndef STATUS_CANNOT_LOAD_REGISTRY_FILE +# define STATUS_CANNOT_LOAD_REGISTRY_FILE ((NTSTATUS) 0xC0000218L) +#endif + +#ifndef STATUS_DEBUG_ATTACH_FAILED +# define STATUS_DEBUG_ATTACH_FAILED ((NTSTATUS) 0xC0000219L) +#endif + +#ifndef STATUS_SYSTEM_PROCESS_TERMINATED +# define STATUS_SYSTEM_PROCESS_TERMINATED ((NTSTATUS) 0xC000021AL) +#endif + +#ifndef STATUS_DATA_NOT_ACCEPTED +# define STATUS_DATA_NOT_ACCEPTED ((NTSTATUS) 0xC000021BL) +#endif + +#ifndef STATUS_NO_BROWSER_SERVERS_FOUND +# define STATUS_NO_BROWSER_SERVERS_FOUND ((NTSTATUS) 0xC000021CL) +#endif + +#ifndef STATUS_VDM_HARD_ERROR +# define STATUS_VDM_HARD_ERROR ((NTSTATUS) 0xC000021DL) +#endif + +#ifndef STATUS_DRIVER_CANCEL_TIMEOUT +# define STATUS_DRIVER_CANCEL_TIMEOUT ((NTSTATUS) 0xC000021EL) +#endif + +#ifndef STATUS_REPLY_MESSAGE_MISMATCH +# define STATUS_REPLY_MESSAGE_MISMATCH ((NTSTATUS) 0xC000021FL) +#endif + +#ifndef STATUS_MAPPED_ALIGNMENT +# define STATUS_MAPPED_ALIGNMENT ((NTSTATUS) 0xC0000220L) +#endif + +#ifndef STATUS_IMAGE_CHECKSUM_MISMATCH +# define STATUS_IMAGE_CHECKSUM_MISMATCH ((NTSTATUS) 0xC0000221L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA +# define STATUS_LOST_WRITEBEHIND_DATA ((NTSTATUS) 0xC0000222L) +#endif + +#ifndef STATUS_CLIENT_SERVER_PARAMETERS_INVALID +# define STATUS_CLIENT_SERVER_PARAMETERS_INVALID ((NTSTATUS) 0xC0000223L) +#endif + +#ifndef STATUS_PASSWORD_MUST_CHANGE +# define STATUS_PASSWORD_MUST_CHANGE ((NTSTATUS) 0xC0000224L) +#endif + +#ifndef STATUS_NOT_FOUND +# define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L) +#endif + +#ifndef STATUS_NOT_TINY_STREAM +# define STATUS_NOT_TINY_STREAM ((NTSTATUS) 0xC0000226L) +#endif + +#ifndef STATUS_RECOVERY_FAILURE +# define STATUS_RECOVERY_FAILURE ((NTSTATUS) 0xC0000227L) +#endif + +#ifndef STATUS_STACK_OVERFLOW_READ +# define STATUS_STACK_OVERFLOW_READ ((NTSTATUS) 0xC0000228L) +#endif + +#ifndef STATUS_FAIL_CHECK +# define STATUS_FAIL_CHECK ((NTSTATUS) 0xC0000229L) +#endif + +#ifndef STATUS_DUPLICATE_OBJECTID +# define STATUS_DUPLICATE_OBJECTID ((NTSTATUS) 0xC000022AL) +#endif + +#ifndef STATUS_OBJECTID_EXISTS +# define STATUS_OBJECTID_EXISTS ((NTSTATUS) 0xC000022BL) +#endif + +#ifndef STATUS_CONVERT_TO_LARGE +# define STATUS_CONVERT_TO_LARGE ((NTSTATUS) 0xC000022CL) +#endif + +#ifndef STATUS_RETRY +# define STATUS_RETRY ((NTSTATUS) 0xC000022DL) +#endif + +#ifndef STATUS_FOUND_OUT_OF_SCOPE +# define STATUS_FOUND_OUT_OF_SCOPE ((NTSTATUS) 0xC000022EL) +#endif + +#ifndef STATUS_ALLOCATE_BUCKET +# define STATUS_ALLOCATE_BUCKET ((NTSTATUS) 0xC000022FL) +#endif + +#ifndef STATUS_PROPSET_NOT_FOUND +# define STATUS_PROPSET_NOT_FOUND ((NTSTATUS) 0xC0000230L) +#endif + +#ifndef STATUS_MARSHALL_OVERFLOW +# define STATUS_MARSHALL_OVERFLOW ((NTSTATUS) 0xC0000231L) +#endif + +#ifndef STATUS_INVALID_VARIANT +# define STATUS_INVALID_VARIANT ((NTSTATUS) 0xC0000232L) +#endif + +#ifndef STATUS_DOMAIN_CONTROLLER_NOT_FOUND +# define STATUS_DOMAIN_CONTROLLER_NOT_FOUND ((NTSTATUS) 0xC0000233L) +#endif + +#ifndef STATUS_ACCOUNT_LOCKED_OUT +# define STATUS_ACCOUNT_LOCKED_OUT ((NTSTATUS) 0xC0000234L) +#endif + +#ifndef STATUS_HANDLE_NOT_CLOSABLE +# define STATUS_HANDLE_NOT_CLOSABLE ((NTSTATUS) 0xC0000235L) +#endif + +#ifndef STATUS_CONNECTION_REFUSED +# define STATUS_CONNECTION_REFUSED ((NTSTATUS) 0xC0000236L) +#endif + +#ifndef STATUS_GRACEFUL_DISCONNECT +# define STATUS_GRACEFUL_DISCONNECT ((NTSTATUS) 0xC0000237L) +#endif + +#ifndef STATUS_ADDRESS_ALREADY_ASSOCIATED +# define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS) 0xC0000238L) +#endif + +#ifndef STATUS_ADDRESS_NOT_ASSOCIATED +# define STATUS_ADDRESS_NOT_ASSOCIATED ((NTSTATUS) 0xC0000239L) +#endif + +#ifndef STATUS_CONNECTION_INVALID +# define STATUS_CONNECTION_INVALID ((NTSTATUS) 0xC000023AL) +#endif + +#ifndef STATUS_CONNECTION_ACTIVE +# define STATUS_CONNECTION_ACTIVE ((NTSTATUS) 0xC000023BL) +#endif + +#ifndef STATUS_NETWORK_UNREACHABLE +# define STATUS_NETWORK_UNREACHABLE ((NTSTATUS) 0xC000023CL) +#endif + +#ifndef STATUS_HOST_UNREACHABLE +# define STATUS_HOST_UNREACHABLE ((NTSTATUS) 0xC000023DL) +#endif + +#ifndef STATUS_PROTOCOL_UNREACHABLE +# define STATUS_PROTOCOL_UNREACHABLE ((NTSTATUS) 0xC000023EL) +#endif + +#ifndef STATUS_PORT_UNREACHABLE +# define STATUS_PORT_UNREACHABLE ((NTSTATUS) 0xC000023FL) +#endif + +#ifndef STATUS_REQUEST_ABORTED +# define STATUS_REQUEST_ABORTED ((NTSTATUS) 0xC0000240L) +#endif + +#ifndef STATUS_CONNECTION_ABORTED +# define STATUS_CONNECTION_ABORTED ((NTSTATUS) 0xC0000241L) +#endif + +#ifndef STATUS_BAD_COMPRESSION_BUFFER +# define STATUS_BAD_COMPRESSION_BUFFER ((NTSTATUS) 0xC0000242L) +#endif + +#ifndef STATUS_USER_MAPPED_FILE +# define STATUS_USER_MAPPED_FILE ((NTSTATUS) 0xC0000243L) +#endif + +#ifndef STATUS_AUDIT_FAILED +# define STATUS_AUDIT_FAILED ((NTSTATUS) 0xC0000244L) +#endif + +#ifndef STATUS_TIMER_RESOLUTION_NOT_SET +# define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS) 0xC0000245L) +#endif + +#ifndef STATUS_CONNECTION_COUNT_LIMIT +# define STATUS_CONNECTION_COUNT_LIMIT ((NTSTATUS) 0xC0000246L) +#endif + +#ifndef STATUS_LOGIN_TIME_RESTRICTION +# define STATUS_LOGIN_TIME_RESTRICTION ((NTSTATUS) 0xC0000247L) +#endif + +#ifndef STATUS_LOGIN_WKSTA_RESTRICTION +# define STATUS_LOGIN_WKSTA_RESTRICTION ((NTSTATUS) 0xC0000248L) +#endif + +#ifndef STATUS_IMAGE_MP_UP_MISMATCH +# define STATUS_IMAGE_MP_UP_MISMATCH ((NTSTATUS) 0xC0000249L) +#endif + +#ifndef STATUS_INSUFFICIENT_LOGON_INFO +# define STATUS_INSUFFICIENT_LOGON_INFO ((NTSTATUS) 0xC0000250L) +#endif + +#ifndef STATUS_BAD_DLL_ENTRYPOINT +# define STATUS_BAD_DLL_ENTRYPOINT ((NTSTATUS) 0xC0000251L) +#endif + +#ifndef STATUS_BAD_SERVICE_ENTRYPOINT +# define STATUS_BAD_SERVICE_ENTRYPOINT ((NTSTATUS) 0xC0000252L) +#endif + +#ifndef STATUS_LPC_REPLY_LOST +# define STATUS_LPC_REPLY_LOST ((NTSTATUS) 0xC0000253L) +#endif + +#ifndef STATUS_IP_ADDRESS_CONFLICT1 +# define STATUS_IP_ADDRESS_CONFLICT1 ((NTSTATUS) 0xC0000254L) +#endif + +#ifndef STATUS_IP_ADDRESS_CONFLICT2 +# define STATUS_IP_ADDRESS_CONFLICT2 ((NTSTATUS) 0xC0000255L) +#endif + +#ifndef STATUS_REGISTRY_QUOTA_LIMIT +# define STATUS_REGISTRY_QUOTA_LIMIT ((NTSTATUS) 0xC0000256L) +#endif + +#ifndef STATUS_PATH_NOT_COVERED +# define STATUS_PATH_NOT_COVERED ((NTSTATUS) 0xC0000257L) +#endif + +#ifndef STATUS_NO_CALLBACK_ACTIVE +# define STATUS_NO_CALLBACK_ACTIVE ((NTSTATUS) 0xC0000258L) +#endif + +#ifndef STATUS_LICENSE_QUOTA_EXCEEDED +# define STATUS_LICENSE_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000259L) +#endif + +#ifndef STATUS_PWD_TOO_SHORT +# define STATUS_PWD_TOO_SHORT ((NTSTATUS) 0xC000025AL) +#endif + +#ifndef STATUS_PWD_TOO_RECENT +# define STATUS_PWD_TOO_RECENT ((NTSTATUS) 0xC000025BL) +#endif + +#ifndef STATUS_PWD_HISTORY_CONFLICT +# define STATUS_PWD_HISTORY_CONFLICT ((NTSTATUS) 0xC000025CL) +#endif + +#ifndef STATUS_PLUGPLAY_NO_DEVICE +# define STATUS_PLUGPLAY_NO_DEVICE ((NTSTATUS) 0xC000025EL) +#endif + +#ifndef STATUS_UNSUPPORTED_COMPRESSION +# define STATUS_UNSUPPORTED_COMPRESSION ((NTSTATUS) 0xC000025FL) +#endif + +#ifndef STATUS_INVALID_HW_PROFILE +# define STATUS_INVALID_HW_PROFILE ((NTSTATUS) 0xC0000260L) +#endif + +#ifndef STATUS_INVALID_PLUGPLAY_DEVICE_PATH +# define STATUS_INVALID_PLUGPLAY_DEVICE_PATH ((NTSTATUS) 0xC0000261L) +#endif + +#ifndef STATUS_DRIVER_ORDINAL_NOT_FOUND +# define STATUS_DRIVER_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000262L) +#endif + +#ifndef STATUS_DRIVER_ENTRYPOINT_NOT_FOUND +# define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000263L) +#endif + +#ifndef STATUS_RESOURCE_NOT_OWNED +# define STATUS_RESOURCE_NOT_OWNED ((NTSTATUS) 0xC0000264L) +#endif + +#ifndef STATUS_TOO_MANY_LINKS +# define STATUS_TOO_MANY_LINKS ((NTSTATUS) 0xC0000265L) +#endif + +#ifndef STATUS_QUOTA_LIST_INCONSISTENT +# define STATUS_QUOTA_LIST_INCONSISTENT ((NTSTATUS) 0xC0000266L) +#endif + +#ifndef STATUS_FILE_IS_OFFLINE +# define STATUS_FILE_IS_OFFLINE ((NTSTATUS) 0xC0000267L) +#endif + +#ifndef STATUS_EVALUATION_EXPIRATION +# define STATUS_EVALUATION_EXPIRATION ((NTSTATUS) 0xC0000268L) +#endif + +#ifndef STATUS_ILLEGAL_DLL_RELOCATION +# define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xC0000269L) +#endif + +#ifndef STATUS_LICENSE_VIOLATION +# define STATUS_LICENSE_VIOLATION ((NTSTATUS) 0xC000026AL) +#endif + +#ifndef STATUS_DLL_INIT_FAILED_LOGOFF +# define STATUS_DLL_INIT_FAILED_LOGOFF ((NTSTATUS) 0xC000026BL) +#endif + +#ifndef STATUS_DRIVER_UNABLE_TO_LOAD +# define STATUS_DRIVER_UNABLE_TO_LOAD ((NTSTATUS) 0xC000026CL) +#endif + +#ifndef STATUS_DFS_UNAVAILABLE +# define STATUS_DFS_UNAVAILABLE ((NTSTATUS) 0xC000026DL) +#endif + +#ifndef STATUS_VOLUME_DISMOUNTED +# define STATUS_VOLUME_DISMOUNTED ((NTSTATUS) 0xC000026EL) +#endif + +#ifndef STATUS_WX86_INTERNAL_ERROR +# define STATUS_WX86_INTERNAL_ERROR ((NTSTATUS) 0xC000026FL) +#endif + +#ifndef STATUS_WX86_FLOAT_STACK_CHECK +# define STATUS_WX86_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000270L) +#endif + +#ifndef STATUS_VALIDATE_CONTINUE +# define STATUS_VALIDATE_CONTINUE ((NTSTATUS) 0xC0000271L) +#endif + +#ifndef STATUS_NO_MATCH +# define STATUS_NO_MATCH ((NTSTATUS) 0xC0000272L) +#endif + +#ifndef STATUS_NO_MORE_MATCHES +# define STATUS_NO_MORE_MATCHES ((NTSTATUS) 0xC0000273L) +#endif + +#ifndef STATUS_NOT_A_REPARSE_POINT +# define STATUS_NOT_A_REPARSE_POINT ((NTSTATUS) 0xC0000275L) +#endif + +#ifndef STATUS_IO_REPARSE_TAG_INVALID +# define STATUS_IO_REPARSE_TAG_INVALID ((NTSTATUS) 0xC0000276L) +#endif + +#ifndef STATUS_IO_REPARSE_TAG_MISMATCH +# define STATUS_IO_REPARSE_TAG_MISMATCH ((NTSTATUS) 0xC0000277L) +#endif + +#ifndef STATUS_IO_REPARSE_DATA_INVALID +# define STATUS_IO_REPARSE_DATA_INVALID ((NTSTATUS) 0xC0000278L) +#endif + +#ifndef STATUS_IO_REPARSE_TAG_NOT_HANDLED +# define STATUS_IO_REPARSE_TAG_NOT_HANDLED ((NTSTATUS) 0xC0000279L) +#endif + +#ifndef STATUS_REPARSE_POINT_NOT_RESOLVED +# define STATUS_REPARSE_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000280L) +#endif + +#ifndef STATUS_DIRECTORY_IS_A_REPARSE_POINT +# define STATUS_DIRECTORY_IS_A_REPARSE_POINT ((NTSTATUS) 0xC0000281L) +#endif + +#ifndef STATUS_RANGE_LIST_CONFLICT +# define STATUS_RANGE_LIST_CONFLICT ((NTSTATUS) 0xC0000282L) +#endif + +#ifndef STATUS_SOURCE_ELEMENT_EMPTY +# define STATUS_SOURCE_ELEMENT_EMPTY ((NTSTATUS) 0xC0000283L) +#endif + +#ifndef STATUS_DESTINATION_ELEMENT_FULL +# define STATUS_DESTINATION_ELEMENT_FULL ((NTSTATUS) 0xC0000284L) +#endif + +#ifndef STATUS_ILLEGAL_ELEMENT_ADDRESS +# define STATUS_ILLEGAL_ELEMENT_ADDRESS ((NTSTATUS) 0xC0000285L) +#endif + +#ifndef STATUS_MAGAZINE_NOT_PRESENT +# define STATUS_MAGAZINE_NOT_PRESENT ((NTSTATUS) 0xC0000286L) +#endif + +#ifndef STATUS_REINITIALIZATION_NEEDED +# define STATUS_REINITIALIZATION_NEEDED ((NTSTATUS) 0xC0000287L) +#endif + +#ifndef STATUS_DEVICE_REQUIRES_CLEANING +# define STATUS_DEVICE_REQUIRES_CLEANING ((NTSTATUS) 0x80000288L) +#endif + +#ifndef STATUS_DEVICE_DOOR_OPEN +# define STATUS_DEVICE_DOOR_OPEN ((NTSTATUS) 0x80000289L) +#endif + +#ifndef STATUS_ENCRYPTION_FAILED +# define STATUS_ENCRYPTION_FAILED ((NTSTATUS) 0xC000028AL) +#endif + +#ifndef STATUS_DECRYPTION_FAILED +# define STATUS_DECRYPTION_FAILED ((NTSTATUS) 0xC000028BL) +#endif + +#ifndef STATUS_RANGE_NOT_FOUND +# define STATUS_RANGE_NOT_FOUND ((NTSTATUS) 0xC000028CL) +#endif + +#ifndef STATUS_NO_RECOVERY_POLICY +# define STATUS_NO_RECOVERY_POLICY ((NTSTATUS) 0xC000028DL) +#endif + +#ifndef STATUS_NO_EFS +# define STATUS_NO_EFS ((NTSTATUS) 0xC000028EL) +#endif + +#ifndef STATUS_WRONG_EFS +# define STATUS_WRONG_EFS ((NTSTATUS) 0xC000028FL) +#endif + +#ifndef STATUS_NO_USER_KEYS +# define STATUS_NO_USER_KEYS ((NTSTATUS) 0xC0000290L) +#endif + +#ifndef STATUS_FILE_NOT_ENCRYPTED +# define STATUS_FILE_NOT_ENCRYPTED ((NTSTATUS) 0xC0000291L) +#endif + +#ifndef STATUS_NOT_EXPORT_FORMAT +# define STATUS_NOT_EXPORT_FORMAT ((NTSTATUS) 0xC0000292L) +#endif + +#ifndef STATUS_FILE_ENCRYPTED +# define STATUS_FILE_ENCRYPTED ((NTSTATUS) 0xC0000293L) +#endif + +#ifndef STATUS_WAKE_SYSTEM +# define STATUS_WAKE_SYSTEM ((NTSTATUS) 0x40000294L) +#endif + +#ifndef STATUS_WMI_GUID_NOT_FOUND +# define STATUS_WMI_GUID_NOT_FOUND ((NTSTATUS) 0xC0000295L) +#endif + +#ifndef STATUS_WMI_INSTANCE_NOT_FOUND +# define STATUS_WMI_INSTANCE_NOT_FOUND ((NTSTATUS) 0xC0000296L) +#endif + +#ifndef STATUS_WMI_ITEMID_NOT_FOUND +# define STATUS_WMI_ITEMID_NOT_FOUND ((NTSTATUS) 0xC0000297L) +#endif + +#ifndef STATUS_WMI_TRY_AGAIN +# define STATUS_WMI_TRY_AGAIN ((NTSTATUS) 0xC0000298L) +#endif + +#ifndef STATUS_SHARED_POLICY +# define STATUS_SHARED_POLICY ((NTSTATUS) 0xC0000299L) +#endif + +#ifndef STATUS_POLICY_OBJECT_NOT_FOUND +# define STATUS_POLICY_OBJECT_NOT_FOUND ((NTSTATUS) 0xC000029AL) +#endif + +#ifndef STATUS_POLICY_ONLY_IN_DS +# define STATUS_POLICY_ONLY_IN_DS ((NTSTATUS) 0xC000029BL) +#endif + +#ifndef STATUS_VOLUME_NOT_UPGRADED +# define STATUS_VOLUME_NOT_UPGRADED ((NTSTATUS) 0xC000029CL) +#endif + +#ifndef STATUS_REMOTE_STORAGE_NOT_ACTIVE +# define STATUS_REMOTE_STORAGE_NOT_ACTIVE ((NTSTATUS) 0xC000029DL) +#endif + +#ifndef STATUS_REMOTE_STORAGE_MEDIA_ERROR +# define STATUS_REMOTE_STORAGE_MEDIA_ERROR ((NTSTATUS) 0xC000029EL) +#endif + +#ifndef STATUS_NO_TRACKING_SERVICE +# define STATUS_NO_TRACKING_SERVICE ((NTSTATUS) 0xC000029FL) +#endif + +#ifndef STATUS_SERVER_SID_MISMATCH +# define STATUS_SERVER_SID_MISMATCH ((NTSTATUS) 0xC00002A0L) +#endif + +#ifndef STATUS_DS_NO_ATTRIBUTE_OR_VALUE +# define STATUS_DS_NO_ATTRIBUTE_OR_VALUE ((NTSTATUS) 0xC00002A1L) +#endif + +#ifndef STATUS_DS_INVALID_ATTRIBUTE_SYNTAX +# define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX ((NTSTATUS) 0xC00002A2L) +#endif + +#ifndef STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED +# define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED ((NTSTATUS) 0xC00002A3L) +#endif + +#ifndef STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS +# define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS ((NTSTATUS) 0xC00002A4L) +#endif + +#ifndef STATUS_DS_BUSY +# define STATUS_DS_BUSY ((NTSTATUS) 0xC00002A5L) +#endif + +#ifndef STATUS_DS_UNAVAILABLE +# define STATUS_DS_UNAVAILABLE ((NTSTATUS) 0xC00002A6L) +#endif + +#ifndef STATUS_DS_NO_RIDS_ALLOCATED +# define STATUS_DS_NO_RIDS_ALLOCATED ((NTSTATUS) 0xC00002A7L) +#endif + +#ifndef STATUS_DS_NO_MORE_RIDS +# define STATUS_DS_NO_MORE_RIDS ((NTSTATUS) 0xC00002A8L) +#endif + +#ifndef STATUS_DS_INCORRECT_ROLE_OWNER +# define STATUS_DS_INCORRECT_ROLE_OWNER ((NTSTATUS) 0xC00002A9L) +#endif + +#ifndef STATUS_DS_RIDMGR_INIT_ERROR +# define STATUS_DS_RIDMGR_INIT_ERROR ((NTSTATUS) 0xC00002AAL) +#endif + +#ifndef STATUS_DS_OBJ_CLASS_VIOLATION +# define STATUS_DS_OBJ_CLASS_VIOLATION ((NTSTATUS) 0xC00002ABL) +#endif + +#ifndef STATUS_DS_CANT_ON_NON_LEAF +# define STATUS_DS_CANT_ON_NON_LEAF ((NTSTATUS) 0xC00002ACL) +#endif + +#ifndef STATUS_DS_CANT_ON_RDN +# define STATUS_DS_CANT_ON_RDN ((NTSTATUS) 0xC00002ADL) +#endif + +#ifndef STATUS_DS_CANT_MOD_OBJ_CLASS +# define STATUS_DS_CANT_MOD_OBJ_CLASS ((NTSTATUS) 0xC00002AEL) +#endif + +#ifndef STATUS_DS_CROSS_DOM_MOVE_FAILED +# define STATUS_DS_CROSS_DOM_MOVE_FAILED ((NTSTATUS) 0xC00002AFL) +#endif + +#ifndef STATUS_DS_GC_NOT_AVAILABLE +# define STATUS_DS_GC_NOT_AVAILABLE ((NTSTATUS) 0xC00002B0L) +#endif + +#ifndef STATUS_DIRECTORY_SERVICE_REQUIRED +# define STATUS_DIRECTORY_SERVICE_REQUIRED ((NTSTATUS) 0xC00002B1L) +#endif + +#ifndef STATUS_REPARSE_ATTRIBUTE_CONFLICT +# define STATUS_REPARSE_ATTRIBUTE_CONFLICT ((NTSTATUS) 0xC00002B2L) +#endif + +#ifndef STATUS_CANT_ENABLE_DENY_ONLY +# define STATUS_CANT_ENABLE_DENY_ONLY ((NTSTATUS) 0xC00002B3L) +#endif + +#ifndef STATUS_FLOAT_MULTIPLE_FAULTS +# define STATUS_FLOAT_MULTIPLE_FAULTS ((NTSTATUS) 0xC00002B4L) +#endif + +#ifndef STATUS_FLOAT_MULTIPLE_TRAPS +# define STATUS_FLOAT_MULTIPLE_TRAPS ((NTSTATUS) 0xC00002B5L) +#endif + +#ifndef STATUS_DEVICE_REMOVED +# define STATUS_DEVICE_REMOVED ((NTSTATUS) 0xC00002B6L) +#endif + +#ifndef STATUS_JOURNAL_DELETE_IN_PROGRESS +# define STATUS_JOURNAL_DELETE_IN_PROGRESS ((NTSTATUS) 0xC00002B7L) +#endif + +#ifndef STATUS_JOURNAL_NOT_ACTIVE +# define STATUS_JOURNAL_NOT_ACTIVE ((NTSTATUS) 0xC00002B8L) +#endif + +#ifndef STATUS_NOINTERFACE +# define STATUS_NOINTERFACE ((NTSTATUS) 0xC00002B9L) +#endif + +#ifndef STATUS_DS_ADMIN_LIMIT_EXCEEDED +# define STATUS_DS_ADMIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00002C1L) +#endif + +#ifndef STATUS_DRIVER_FAILED_SLEEP +# define STATUS_DRIVER_FAILED_SLEEP ((NTSTATUS) 0xC00002C2L) +#endif + +#ifndef STATUS_MUTUAL_AUTHENTICATION_FAILED +# define STATUS_MUTUAL_AUTHENTICATION_FAILED ((NTSTATUS) 0xC00002C3L) +#endif + +#ifndef STATUS_CORRUPT_SYSTEM_FILE +# define STATUS_CORRUPT_SYSTEM_FILE ((NTSTATUS) 0xC00002C4L) +#endif + +#ifndef STATUS_DATATYPE_MISALIGNMENT_ERROR +# define STATUS_DATATYPE_MISALIGNMENT_ERROR ((NTSTATUS) 0xC00002C5L) +#endif + +#ifndef STATUS_WMI_READ_ONLY +# define STATUS_WMI_READ_ONLY ((NTSTATUS) 0xC00002C6L) +#endif + +#ifndef STATUS_WMI_SET_FAILURE +# define STATUS_WMI_SET_FAILURE ((NTSTATUS) 0xC00002C7L) +#endif + +#ifndef STATUS_COMMITMENT_MINIMUM +# define STATUS_COMMITMENT_MINIMUM ((NTSTATUS) 0xC00002C8L) +#endif + +#ifndef STATUS_REG_NAT_CONSUMPTION +# define STATUS_REG_NAT_CONSUMPTION ((NTSTATUS) 0xC00002C9L) +#endif + +#ifndef STATUS_TRANSPORT_FULL +# define STATUS_TRANSPORT_FULL ((NTSTATUS) 0xC00002CAL) +#endif + +#ifndef STATUS_DS_SAM_INIT_FAILURE +# define STATUS_DS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002CBL) +#endif + +#ifndef STATUS_ONLY_IF_CONNECTED +# define STATUS_ONLY_IF_CONNECTED ((NTSTATUS) 0xC00002CCL) +#endif + +#ifndef STATUS_DS_SENSITIVE_GROUP_VIOLATION +# define STATUS_DS_SENSITIVE_GROUP_VIOLATION ((NTSTATUS) 0xC00002CDL) +#endif + +#ifndef STATUS_PNP_RESTART_ENUMERATION +# define STATUS_PNP_RESTART_ENUMERATION ((NTSTATUS) 0xC00002CEL) +#endif + +#ifndef STATUS_JOURNAL_ENTRY_DELETED +# define STATUS_JOURNAL_ENTRY_DELETED ((NTSTATUS) 0xC00002CFL) +#endif + +#ifndef STATUS_DS_CANT_MOD_PRIMARYGROUPID +# define STATUS_DS_CANT_MOD_PRIMARYGROUPID ((NTSTATUS) 0xC00002D0L) +#endif + +#ifndef STATUS_SYSTEM_IMAGE_BAD_SIGNATURE +# define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE ((NTSTATUS) 0xC00002D1L) +#endif + +#ifndef STATUS_PNP_REBOOT_REQUIRED +# define STATUS_PNP_REBOOT_REQUIRED ((NTSTATUS) 0xC00002D2L) +#endif + +#ifndef STATUS_POWER_STATE_INVALID +# define STATUS_POWER_STATE_INVALID ((NTSTATUS) 0xC00002D3L) +#endif + +#ifndef STATUS_DS_INVALID_GROUP_TYPE +# define STATUS_DS_INVALID_GROUP_TYPE ((NTSTATUS) 0xC00002D4L) +#endif + +#ifndef STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN +# define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D5L) +#endif + +#ifndef STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN +# define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D6L) +#endif + +#ifndef STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER +# define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D7L) +#endif + +#ifndef STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER +# define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC00002D8L) +#endif + +#ifndef STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER +# define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D9L) +#endif + +#ifndef STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER +# define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER ((NTSTATUS) 0xC00002DAL) +#endif + +#ifndef STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER +# define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER ((NTSTATUS) 0xC00002DBL) +#endif + +#ifndef STATUS_DS_HAVE_PRIMARY_MEMBERS +# define STATUS_DS_HAVE_PRIMARY_MEMBERS ((NTSTATUS) 0xC00002DCL) +#endif + +#ifndef STATUS_WMI_NOT_SUPPORTED +# define STATUS_WMI_NOT_SUPPORTED ((NTSTATUS) 0xC00002DDL) +#endif + +#ifndef STATUS_INSUFFICIENT_POWER +# define STATUS_INSUFFICIENT_POWER ((NTSTATUS) 0xC00002DEL) +#endif + +#ifndef STATUS_SAM_NEED_BOOTKEY_PASSWORD +# define STATUS_SAM_NEED_BOOTKEY_PASSWORD ((NTSTATUS) 0xC00002DFL) +#endif + +#ifndef STATUS_SAM_NEED_BOOTKEY_FLOPPY +# define STATUS_SAM_NEED_BOOTKEY_FLOPPY ((NTSTATUS) 0xC00002E0L) +#endif + +#ifndef STATUS_DS_CANT_START +# define STATUS_DS_CANT_START ((NTSTATUS) 0xC00002E1L) +#endif + +#ifndef STATUS_DS_INIT_FAILURE +# define STATUS_DS_INIT_FAILURE ((NTSTATUS) 0xC00002E2L) +#endif + +#ifndef STATUS_SAM_INIT_FAILURE +# define STATUS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002E3L) +#endif + +#ifndef STATUS_DS_GC_REQUIRED +# define STATUS_DS_GC_REQUIRED ((NTSTATUS) 0xC00002E4L) +#endif + +#ifndef STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY +# define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY ((NTSTATUS) 0xC00002E5L) +#endif + +#ifndef STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS +# define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS ((NTSTATUS) 0xC00002E6L) +#endif + +#ifndef STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED +# define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED ((NTSTATUS) 0xC00002E7L) +#endif + +#ifndef STATUS_MULTIPLE_FAULT_VIOLATION +# define STATUS_MULTIPLE_FAULT_VIOLATION ((NTSTATUS) 0xC00002E8L) +#endif + +#ifndef STATUS_CURRENT_DOMAIN_NOT_ALLOWED +# define STATUS_CURRENT_DOMAIN_NOT_ALLOWED ((NTSTATUS) 0xC00002E9L) +#endif + +#ifndef STATUS_CANNOT_MAKE +# define STATUS_CANNOT_MAKE ((NTSTATUS) 0xC00002EAL) +#endif + +#ifndef STATUS_SYSTEM_SHUTDOWN +# define STATUS_SYSTEM_SHUTDOWN ((NTSTATUS) 0xC00002EBL) +#endif + +#ifndef STATUS_DS_INIT_FAILURE_CONSOLE +# define STATUS_DS_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002ECL) +#endif + +#ifndef STATUS_DS_SAM_INIT_FAILURE_CONSOLE +# define STATUS_DS_SAM_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002EDL) +#endif + +#ifndef STATUS_UNFINISHED_CONTEXT_DELETED +# define STATUS_UNFINISHED_CONTEXT_DELETED ((NTSTATUS) 0xC00002EEL) +#endif + +#ifndef STATUS_NO_TGT_REPLY +# define STATUS_NO_TGT_REPLY ((NTSTATUS) 0xC00002EFL) +#endif + +#ifndef STATUS_OBJECTID_NOT_FOUND +# define STATUS_OBJECTID_NOT_FOUND ((NTSTATUS) 0xC00002F0L) +#endif + +#ifndef STATUS_NO_IP_ADDRESSES +# define STATUS_NO_IP_ADDRESSES ((NTSTATUS) 0xC00002F1L) +#endif + +#ifndef STATUS_WRONG_CREDENTIAL_HANDLE +# define STATUS_WRONG_CREDENTIAL_HANDLE ((NTSTATUS) 0xC00002F2L) +#endif + +#ifndef STATUS_CRYPTO_SYSTEM_INVALID +# define STATUS_CRYPTO_SYSTEM_INVALID ((NTSTATUS) 0xC00002F3L) +#endif + +#ifndef STATUS_MAX_REFERRALS_EXCEEDED +# define STATUS_MAX_REFERRALS_EXCEEDED ((NTSTATUS) 0xC00002F4L) +#endif + +#ifndef STATUS_MUST_BE_KDC +# define STATUS_MUST_BE_KDC ((NTSTATUS) 0xC00002F5L) +#endif + +#ifndef STATUS_STRONG_CRYPTO_NOT_SUPPORTED +# define STATUS_STRONG_CRYPTO_NOT_SUPPORTED ((NTSTATUS) 0xC00002F6L) +#endif + +#ifndef STATUS_TOO_MANY_PRINCIPALS +# define STATUS_TOO_MANY_PRINCIPALS ((NTSTATUS) 0xC00002F7L) +#endif + +#ifndef STATUS_NO_PA_DATA +# define STATUS_NO_PA_DATA ((NTSTATUS) 0xC00002F8L) +#endif + +#ifndef STATUS_PKINIT_NAME_MISMATCH +# define STATUS_PKINIT_NAME_MISMATCH ((NTSTATUS) 0xC00002F9L) +#endif + +#ifndef STATUS_SMARTCARD_LOGON_REQUIRED +# define STATUS_SMARTCARD_LOGON_REQUIRED ((NTSTATUS) 0xC00002FAL) +#endif + +#ifndef STATUS_KDC_INVALID_REQUEST +# define STATUS_KDC_INVALID_REQUEST ((NTSTATUS) 0xC00002FBL) +#endif + +#ifndef STATUS_KDC_UNABLE_TO_REFER +# define STATUS_KDC_UNABLE_TO_REFER ((NTSTATUS) 0xC00002FCL) +#endif + +#ifndef STATUS_KDC_UNKNOWN_ETYPE +# define STATUS_KDC_UNKNOWN_ETYPE ((NTSTATUS) 0xC00002FDL) +#endif + +#ifndef STATUS_SHUTDOWN_IN_PROGRESS +# define STATUS_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FEL) +#endif + +#ifndef STATUS_SERVER_SHUTDOWN_IN_PROGRESS +# define STATUS_SERVER_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FFL) +#endif + +#ifndef STATUS_NOT_SUPPORTED_ON_SBS +# define STATUS_NOT_SUPPORTED_ON_SBS ((NTSTATUS) 0xC0000300L) +#endif + +#ifndef STATUS_WMI_GUID_DISCONNECTED +# define STATUS_WMI_GUID_DISCONNECTED ((NTSTATUS) 0xC0000301L) +#endif + +#ifndef STATUS_WMI_ALREADY_DISABLED +# define STATUS_WMI_ALREADY_DISABLED ((NTSTATUS) 0xC0000302L) +#endif + +#ifndef STATUS_WMI_ALREADY_ENABLED +# define STATUS_WMI_ALREADY_ENABLED ((NTSTATUS) 0xC0000303L) +#endif + +#ifndef STATUS_MFT_TOO_FRAGMENTED +# define STATUS_MFT_TOO_FRAGMENTED ((NTSTATUS) 0xC0000304L) +#endif + +#ifndef STATUS_COPY_PROTECTION_FAILURE +# define STATUS_COPY_PROTECTION_FAILURE ((NTSTATUS) 0xC0000305L) +#endif + +#ifndef STATUS_CSS_AUTHENTICATION_FAILURE +# define STATUS_CSS_AUTHENTICATION_FAILURE ((NTSTATUS) 0xC0000306L) +#endif + +#ifndef STATUS_CSS_KEY_NOT_PRESENT +# define STATUS_CSS_KEY_NOT_PRESENT ((NTSTATUS) 0xC0000307L) +#endif + +#ifndef STATUS_CSS_KEY_NOT_ESTABLISHED +# define STATUS_CSS_KEY_NOT_ESTABLISHED ((NTSTATUS) 0xC0000308L) +#endif + +#ifndef STATUS_CSS_SCRAMBLED_SECTOR +# define STATUS_CSS_SCRAMBLED_SECTOR ((NTSTATUS) 0xC0000309L) +#endif + +#ifndef STATUS_CSS_REGION_MISMATCH +# define STATUS_CSS_REGION_MISMATCH ((NTSTATUS) 0xC000030AL) +#endif + +#ifndef STATUS_CSS_RESETS_EXHAUSTED +# define STATUS_CSS_RESETS_EXHAUSTED ((NTSTATUS) 0xC000030BL) +#endif + +#ifndef STATUS_PKINIT_FAILURE +# define STATUS_PKINIT_FAILURE ((NTSTATUS) 0xC0000320L) +#endif + +#ifndef STATUS_SMARTCARD_SUBSYSTEM_FAILURE +# define STATUS_SMARTCARD_SUBSYSTEM_FAILURE ((NTSTATUS) 0xC0000321L) +#endif + +#ifndef STATUS_NO_KERB_KEY +# define STATUS_NO_KERB_KEY ((NTSTATUS) 0xC0000322L) +#endif + +#ifndef STATUS_HOST_DOWN +# define STATUS_HOST_DOWN ((NTSTATUS) 0xC0000350L) +#endif + +#ifndef STATUS_UNSUPPORTED_PREAUTH +# define STATUS_UNSUPPORTED_PREAUTH ((NTSTATUS) 0xC0000351L) +#endif + +#ifndef STATUS_EFS_ALG_BLOB_TOO_BIG +# define STATUS_EFS_ALG_BLOB_TOO_BIG ((NTSTATUS) 0xC0000352L) +#endif + +#ifndef STATUS_PORT_NOT_SET +# define STATUS_PORT_NOT_SET ((NTSTATUS) 0xC0000353L) +#endif + +#ifndef STATUS_DEBUGGER_INACTIVE +# define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354L) +#endif + +#ifndef STATUS_DS_VERSION_CHECK_FAILURE +# define STATUS_DS_VERSION_CHECK_FAILURE ((NTSTATUS) 0xC0000355L) +#endif + +#ifndef STATUS_AUDITING_DISABLED +# define STATUS_AUDITING_DISABLED ((NTSTATUS) 0xC0000356L) +#endif + +#ifndef STATUS_PRENT4_MACHINE_ACCOUNT +# define STATUS_PRENT4_MACHINE_ACCOUNT ((NTSTATUS) 0xC0000357L) +#endif + +#ifndef STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER +# define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC0000358L) +#endif + +#ifndef STATUS_INVALID_IMAGE_WIN_32 +# define STATUS_INVALID_IMAGE_WIN_32 ((NTSTATUS) 0xC0000359L) +#endif + +#ifndef STATUS_INVALID_IMAGE_WIN_64 +# define STATUS_INVALID_IMAGE_WIN_64 ((NTSTATUS) 0xC000035AL) +#endif + +#ifndef STATUS_BAD_BINDINGS +# define STATUS_BAD_BINDINGS ((NTSTATUS) 0xC000035BL) +#endif + +#ifndef STATUS_NETWORK_SESSION_EXPIRED +# define STATUS_NETWORK_SESSION_EXPIRED ((NTSTATUS) 0xC000035CL) +#endif + +#ifndef STATUS_APPHELP_BLOCK +# define STATUS_APPHELP_BLOCK ((NTSTATUS) 0xC000035DL) +#endif + +#ifndef STATUS_ALL_SIDS_FILTERED +# define STATUS_ALL_SIDS_FILTERED ((NTSTATUS) 0xC000035EL) +#endif + +#ifndef STATUS_NOT_SAFE_MODE_DRIVER +# define STATUS_NOT_SAFE_MODE_DRIVER ((NTSTATUS) 0xC000035FL) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT +# define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT ((NTSTATUS) 0xC0000361L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PATH +# define STATUS_ACCESS_DISABLED_BY_POLICY_PATH ((NTSTATUS) 0xC0000362L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER +# define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER ((NTSTATUS) 0xC0000363L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_OTHER +# define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER ((NTSTATUS) 0xC0000364L) +#endif + +#ifndef STATUS_FAILED_DRIVER_ENTRY +# define STATUS_FAILED_DRIVER_ENTRY ((NTSTATUS) 0xC0000365L) +#endif + +#ifndef STATUS_DEVICE_ENUMERATION_ERROR +# define STATUS_DEVICE_ENUMERATION_ERROR ((NTSTATUS) 0xC0000366L) +#endif + +#ifndef STATUS_MOUNT_POINT_NOT_RESOLVED +# define STATUS_MOUNT_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000368L) +#endif + +#ifndef STATUS_INVALID_DEVICE_OBJECT_PARAMETER +# define STATUS_INVALID_DEVICE_OBJECT_PARAMETER ((NTSTATUS) 0xC0000369L) +#endif + +#ifndef STATUS_MCA_OCCURED +# define STATUS_MCA_OCCURED ((NTSTATUS) 0xC000036AL) +#endif + +#ifndef STATUS_DRIVER_BLOCKED_CRITICAL +# define STATUS_DRIVER_BLOCKED_CRITICAL ((NTSTATUS) 0xC000036BL) +#endif + +#ifndef STATUS_DRIVER_BLOCKED +# define STATUS_DRIVER_BLOCKED ((NTSTATUS) 0xC000036CL) +#endif + +#ifndef STATUS_DRIVER_DATABASE_ERROR +# define STATUS_DRIVER_DATABASE_ERROR ((NTSTATUS) 0xC000036DL) +#endif + +#ifndef STATUS_SYSTEM_HIVE_TOO_LARGE +# define STATUS_SYSTEM_HIVE_TOO_LARGE ((NTSTATUS) 0xC000036EL) +#endif + +#ifndef STATUS_INVALID_IMPORT_OF_NON_DLL +# define STATUS_INVALID_IMPORT_OF_NON_DLL ((NTSTATUS) 0xC000036FL) +#endif + +#ifndef STATUS_DS_SHUTTING_DOWN +# define STATUS_DS_SHUTTING_DOWN ((NTSTATUS) 0x40000370L) +#endif + +#ifndef STATUS_NO_SECRETS +# define STATUS_NO_SECRETS ((NTSTATUS) 0xC0000371L) +#endif + +#ifndef STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY +# define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY ((NTSTATUS) 0xC0000372L) +#endif + +#ifndef STATUS_FAILED_STACK_SWITCH +# define STATUS_FAILED_STACK_SWITCH ((NTSTATUS) 0xC0000373L) +#endif + +#ifndef STATUS_HEAP_CORRUPTION +# define STATUS_HEAP_CORRUPTION ((NTSTATUS) 0xC0000374L) +#endif + +#ifndef STATUS_SMARTCARD_WRONG_PIN +# define STATUS_SMARTCARD_WRONG_PIN ((NTSTATUS) 0xC0000380L) +#endif + +#ifndef STATUS_SMARTCARD_CARD_BLOCKED +# define STATUS_SMARTCARD_CARD_BLOCKED ((NTSTATUS) 0xC0000381L) +#endif + +#ifndef STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED +# define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED ((NTSTATUS) 0xC0000382L) +#endif + +#ifndef STATUS_SMARTCARD_NO_CARD +# define STATUS_SMARTCARD_NO_CARD ((NTSTATUS) 0xC0000383L) +#endif + +#ifndef STATUS_SMARTCARD_NO_KEY_CONTAINER +# define STATUS_SMARTCARD_NO_KEY_CONTAINER ((NTSTATUS) 0xC0000384L) +#endif + +#ifndef STATUS_SMARTCARD_NO_CERTIFICATE +# define STATUS_SMARTCARD_NO_CERTIFICATE ((NTSTATUS) 0xC0000385L) +#endif + +#ifndef STATUS_SMARTCARD_NO_KEYSET +# define STATUS_SMARTCARD_NO_KEYSET ((NTSTATUS) 0xC0000386L) +#endif + +#ifndef STATUS_SMARTCARD_IO_ERROR +# define STATUS_SMARTCARD_IO_ERROR ((NTSTATUS) 0xC0000387L) +#endif + +#ifndef STATUS_DOWNGRADE_DETECTED +# define STATUS_DOWNGRADE_DETECTED ((NTSTATUS) 0xC0000388L) +#endif + +#ifndef STATUS_SMARTCARD_CERT_REVOKED +# define STATUS_SMARTCARD_CERT_REVOKED ((NTSTATUS) 0xC0000389L) +#endif + +#ifndef STATUS_ISSUING_CA_UNTRUSTED +# define STATUS_ISSUING_CA_UNTRUSTED ((NTSTATUS) 0xC000038AL) +#endif + +#ifndef STATUS_REVOCATION_OFFLINE_C +# define STATUS_REVOCATION_OFFLINE_C ((NTSTATUS) 0xC000038BL) +#endif + +#ifndef STATUS_PKINIT_CLIENT_FAILURE +# define STATUS_PKINIT_CLIENT_FAILURE ((NTSTATUS) 0xC000038CL) +#endif + +#ifndef STATUS_SMARTCARD_CERT_EXPIRED +# define STATUS_SMARTCARD_CERT_EXPIRED ((NTSTATUS) 0xC000038DL) +#endif + +#ifndef STATUS_DRIVER_FAILED_PRIOR_UNLOAD +# define STATUS_DRIVER_FAILED_PRIOR_UNLOAD ((NTSTATUS) 0xC000038EL) +#endif + +#ifndef STATUS_SMARTCARD_SILENT_CONTEXT +# define STATUS_SMARTCARD_SILENT_CONTEXT ((NTSTATUS) 0xC000038FL) +#endif + +#ifndef STATUS_PER_USER_TRUST_QUOTA_EXCEEDED +# define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000401L) +#endif + +#ifndef STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED +# define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000402L) +#endif + +#ifndef STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED +# define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000403L) +#endif + +#ifndef STATUS_DS_NAME_NOT_UNIQUE +# define STATUS_DS_NAME_NOT_UNIQUE ((NTSTATUS) 0xC0000404L) +#endif + +#ifndef STATUS_DS_DUPLICATE_ID_FOUND +# define STATUS_DS_DUPLICATE_ID_FOUND ((NTSTATUS) 0xC0000405L) +#endif + +#ifndef STATUS_DS_GROUP_CONVERSION_ERROR +# define STATUS_DS_GROUP_CONVERSION_ERROR ((NTSTATUS) 0xC0000406L) +#endif + +#ifndef STATUS_VOLSNAP_PREPARE_HIBERNATE +# define STATUS_VOLSNAP_PREPARE_HIBERNATE ((NTSTATUS) 0xC0000407L) +#endif + +#ifndef STATUS_USER2USER_REQUIRED +# define STATUS_USER2USER_REQUIRED ((NTSTATUS) 0xC0000408L) +#endif + +#ifndef STATUS_STACK_BUFFER_OVERRUN +# define STATUS_STACK_BUFFER_OVERRUN ((NTSTATUS) 0xC0000409L) +#endif + +#ifndef STATUS_NO_S4U_PROT_SUPPORT +# define STATUS_NO_S4U_PROT_SUPPORT ((NTSTATUS) 0xC000040AL) +#endif + +#ifndef STATUS_CROSSREALM_DELEGATION_FAILURE +# define STATUS_CROSSREALM_DELEGATION_FAILURE ((NTSTATUS) 0xC000040BL) +#endif + +#ifndef STATUS_REVOCATION_OFFLINE_KDC +# define STATUS_REVOCATION_OFFLINE_KDC ((NTSTATUS) 0xC000040CL) +#endif + +#ifndef STATUS_ISSUING_CA_UNTRUSTED_KDC +# define STATUS_ISSUING_CA_UNTRUSTED_KDC ((NTSTATUS) 0xC000040DL) +#endif + +#ifndef STATUS_KDC_CERT_EXPIRED +# define STATUS_KDC_CERT_EXPIRED ((NTSTATUS) 0xC000040EL) +#endif + +#ifndef STATUS_KDC_CERT_REVOKED +# define STATUS_KDC_CERT_REVOKED ((NTSTATUS) 0xC000040FL) +#endif + +#ifndef STATUS_PARAMETER_QUOTA_EXCEEDED +# define STATUS_PARAMETER_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000410L) +#endif + +#ifndef STATUS_HIBERNATION_FAILURE +# define STATUS_HIBERNATION_FAILURE ((NTSTATUS) 0xC0000411L) +#endif + +#ifndef STATUS_DELAY_LOAD_FAILED +# define STATUS_DELAY_LOAD_FAILED ((NTSTATUS) 0xC0000412L) +#endif + +#ifndef STATUS_AUTHENTICATION_FIREWALL_FAILED +# define STATUS_AUTHENTICATION_FIREWALL_FAILED ((NTSTATUS) 0xC0000413L) +#endif + +#ifndef STATUS_VDM_DISALLOWED +# define STATUS_VDM_DISALLOWED ((NTSTATUS) 0xC0000414L) +#endif + +#ifndef STATUS_HUNG_DISPLAY_DRIVER_THREAD +# define STATUS_HUNG_DISPLAY_DRIVER_THREAD ((NTSTATUS) 0xC0000415L) +#endif + +#ifndef STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE +# define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE ((NTSTATUS) 0xC0000416L) +#endif + +#ifndef STATUS_INVALID_CRUNTIME_PARAMETER +# define STATUS_INVALID_CRUNTIME_PARAMETER ((NTSTATUS) 0xC0000417L) +#endif + +#ifndef STATUS_NTLM_BLOCKED +# define STATUS_NTLM_BLOCKED ((NTSTATUS) 0xC0000418L) +#endif + +#ifndef STATUS_DS_SRC_SID_EXISTS_IN_FOREST +# define STATUS_DS_SRC_SID_EXISTS_IN_FOREST ((NTSTATUS) 0xC0000419L) +#endif + +#ifndef STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST +# define STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041AL) +#endif + +#ifndef STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST +# define STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041BL) +#endif + +#ifndef STATUS_INVALID_USER_PRINCIPAL_NAME +# define STATUS_INVALID_USER_PRINCIPAL_NAME ((NTSTATUS) 0xC000041CL) +#endif + +#ifndef STATUS_FATAL_USER_CALLBACK_EXCEPTION +# define STATUS_FATAL_USER_CALLBACK_EXCEPTION ((NTSTATUS) 0xC000041DL) +#endif + +#ifndef STATUS_ASSERTION_FAILURE +# define STATUS_ASSERTION_FAILURE ((NTSTATUS) 0xC0000420L) +#endif + +#ifndef STATUS_VERIFIER_STOP +# define STATUS_VERIFIER_STOP ((NTSTATUS) 0xC0000421L) +#endif + +#ifndef STATUS_CALLBACK_POP_STACK +# define STATUS_CALLBACK_POP_STACK ((NTSTATUS) 0xC0000423L) +#endif + +#ifndef STATUS_INCOMPATIBLE_DRIVER_BLOCKED +# define STATUS_INCOMPATIBLE_DRIVER_BLOCKED ((NTSTATUS) 0xC0000424L) +#endif + +#ifndef STATUS_HIVE_UNLOADED +# define STATUS_HIVE_UNLOADED ((NTSTATUS) 0xC0000425L) +#endif + +#ifndef STATUS_COMPRESSION_DISABLED +# define STATUS_COMPRESSION_DISABLED ((NTSTATUS) 0xC0000426L) +#endif + +#ifndef STATUS_FILE_SYSTEM_LIMITATION +# define STATUS_FILE_SYSTEM_LIMITATION ((NTSTATUS) 0xC0000427L) +#endif + +#ifndef STATUS_INVALID_IMAGE_HASH +# define STATUS_INVALID_IMAGE_HASH ((NTSTATUS) 0xC0000428L) +#endif + +#ifndef STATUS_NOT_CAPABLE +# define STATUS_NOT_CAPABLE ((NTSTATUS) 0xC0000429L) +#endif + +#ifndef STATUS_REQUEST_OUT_OF_SEQUENCE +# define STATUS_REQUEST_OUT_OF_SEQUENCE ((NTSTATUS) 0xC000042AL) +#endif + +#ifndef STATUS_IMPLEMENTATION_LIMIT +# define STATUS_IMPLEMENTATION_LIMIT ((NTSTATUS) 0xC000042BL) +#endif + +#ifndef STATUS_ELEVATION_REQUIRED +# define STATUS_ELEVATION_REQUIRED ((NTSTATUS) 0xC000042CL) +#endif + +#ifndef STATUS_NO_SECURITY_CONTEXT +# define STATUS_NO_SECURITY_CONTEXT ((NTSTATUS) 0xC000042DL) +#endif + +#ifndef STATUS_PKU2U_CERT_FAILURE +# define STATUS_PKU2U_CERT_FAILURE ((NTSTATUS) 0xC000042FL) +#endif + +#ifndef STATUS_BEYOND_VDL +# define STATUS_BEYOND_VDL ((NTSTATUS) 0xC0000432L) +#endif + +#ifndef STATUS_ENCOUNTERED_WRITE_IN_PROGRESS +# define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS ((NTSTATUS) 0xC0000433L) +#endif + +#ifndef STATUS_PTE_CHANGED +# define STATUS_PTE_CHANGED ((NTSTATUS) 0xC0000434L) +#endif + +#ifndef STATUS_PURGE_FAILED +# define STATUS_PURGE_FAILED ((NTSTATUS) 0xC0000435L) +#endif + +#ifndef STATUS_CRED_REQUIRES_CONFIRMATION +# define STATUS_CRED_REQUIRES_CONFIRMATION ((NTSTATUS) 0xC0000440L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE +# define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE ((NTSTATUS) 0xC0000441L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER +# define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER ((NTSTATUS) 0xC0000442L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE +# define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE ((NTSTATUS) 0xC0000443L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE +# define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE ((NTSTATUS) 0xC0000444L) +#endif + +#ifndef STATUS_CS_ENCRYPTION_FILE_NOT_CSE +# define STATUS_CS_ENCRYPTION_FILE_NOT_CSE ((NTSTATUS) 0xC0000445L) +#endif + +#ifndef STATUS_INVALID_LABEL +# define STATUS_INVALID_LABEL ((NTSTATUS) 0xC0000446L) +#endif + +#ifndef STATUS_DRIVER_PROCESS_TERMINATED +# define STATUS_DRIVER_PROCESS_TERMINATED ((NTSTATUS) 0xC0000450L) +#endif + +#ifndef STATUS_AMBIGUOUS_SYSTEM_DEVICE +# define STATUS_AMBIGUOUS_SYSTEM_DEVICE ((NTSTATUS) 0xC0000451L) +#endif + +#ifndef STATUS_SYSTEM_DEVICE_NOT_FOUND +# define STATUS_SYSTEM_DEVICE_NOT_FOUND ((NTSTATUS) 0xC0000452L) +#endif + +#ifndef STATUS_RESTART_BOOT_APPLICATION +# define STATUS_RESTART_BOOT_APPLICATION ((NTSTATUS) 0xC0000453L) +#endif + +#ifndef STATUS_INSUFFICIENT_NVRAM_RESOURCES +# define STATUS_INSUFFICIENT_NVRAM_RESOURCES ((NTSTATUS) 0xC0000454L) +#endif + +#ifndef STATUS_INVALID_TASK_NAME +# define STATUS_INVALID_TASK_NAME ((NTSTATUS) 0xC0000500L) +#endif + +#ifndef STATUS_INVALID_TASK_INDEX +# define STATUS_INVALID_TASK_INDEX ((NTSTATUS) 0xC0000501L) +#endif + +#ifndef STATUS_THREAD_ALREADY_IN_TASK +# define STATUS_THREAD_ALREADY_IN_TASK ((NTSTATUS) 0xC0000502L) +#endif + +#ifndef STATUS_CALLBACK_BYPASS +# define STATUS_CALLBACK_BYPASS ((NTSTATUS) 0xC0000503L) +#endif + +#ifndef STATUS_FAIL_FAST_EXCEPTION +# define STATUS_FAIL_FAST_EXCEPTION ((NTSTATUS) 0xC0000602L) +#endif + +#ifndef STATUS_IMAGE_CERT_REVOKED +# define STATUS_IMAGE_CERT_REVOKED ((NTSTATUS) 0xC0000603L) +#endif + +#ifndef STATUS_PORT_CLOSED +# define STATUS_PORT_CLOSED ((NTSTATUS) 0xC0000700L) +#endif + +#ifndef STATUS_MESSAGE_LOST +# define STATUS_MESSAGE_LOST ((NTSTATUS) 0xC0000701L) +#endif + +#ifndef STATUS_INVALID_MESSAGE +# define STATUS_INVALID_MESSAGE ((NTSTATUS) 0xC0000702L) +#endif + +#ifndef STATUS_REQUEST_CANCELED +# define STATUS_REQUEST_CANCELED ((NTSTATUS) 0xC0000703L) +#endif + +#ifndef STATUS_RECURSIVE_DISPATCH +# define STATUS_RECURSIVE_DISPATCH ((NTSTATUS) 0xC0000704L) +#endif + +#ifndef STATUS_LPC_RECEIVE_BUFFER_EXPECTED +# define STATUS_LPC_RECEIVE_BUFFER_EXPECTED ((NTSTATUS) 0xC0000705L) +#endif + +#ifndef STATUS_LPC_INVALID_CONNECTION_USAGE +# define STATUS_LPC_INVALID_CONNECTION_USAGE ((NTSTATUS) 0xC0000706L) +#endif + +#ifndef STATUS_LPC_REQUESTS_NOT_ALLOWED +# define STATUS_LPC_REQUESTS_NOT_ALLOWED ((NTSTATUS) 0xC0000707L) +#endif + +#ifndef STATUS_RESOURCE_IN_USE +# define STATUS_RESOURCE_IN_USE ((NTSTATUS) 0xC0000708L) +#endif + +#ifndef STATUS_HARDWARE_MEMORY_ERROR +# define STATUS_HARDWARE_MEMORY_ERROR ((NTSTATUS) 0xC0000709L) +#endif + +#ifndef STATUS_THREADPOOL_HANDLE_EXCEPTION +# define STATUS_THREADPOOL_HANDLE_EXCEPTION ((NTSTATUS) 0xC000070AL) +#endif + +#ifndef STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070BL) +#endif + +#ifndef STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070CL) +#endif + +#ifndef STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070DL) +#endif + +#ifndef STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED +# define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070EL) +#endif + +#ifndef STATUS_THREADPOOL_RELEASED_DURING_OPERATION +# define STATUS_THREADPOOL_RELEASED_DURING_OPERATION ((NTSTATUS) 0xC000070FL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING +# define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000710L) +#endif + +#ifndef STATUS_APC_RETURNED_WHILE_IMPERSONATING +# define STATUS_APC_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000711L) +#endif + +#ifndef STATUS_PROCESS_IS_PROTECTED +# define STATUS_PROCESS_IS_PROTECTED ((NTSTATUS) 0xC0000712L) +#endif + +#ifndef STATUS_MCA_EXCEPTION +# define STATUS_MCA_EXCEPTION ((NTSTATUS) 0xC0000713L) +#endif + +#ifndef STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE +# define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE ((NTSTATUS) 0xC0000714L) +#endif + +#ifndef STATUS_SYMLINK_CLASS_DISABLED +# define STATUS_SYMLINK_CLASS_DISABLED ((NTSTATUS) 0xC0000715L) +#endif + +#ifndef STATUS_INVALID_IDN_NORMALIZATION +# define STATUS_INVALID_IDN_NORMALIZATION ((NTSTATUS) 0xC0000716L) +#endif + +#ifndef STATUS_NO_UNICODE_TRANSLATION +# define STATUS_NO_UNICODE_TRANSLATION ((NTSTATUS) 0xC0000717L) +#endif + +#ifndef STATUS_ALREADY_REGISTERED +# define STATUS_ALREADY_REGISTERED ((NTSTATUS) 0xC0000718L) +#endif + +#ifndef STATUS_CONTEXT_MISMATCH +# define STATUS_CONTEXT_MISMATCH ((NTSTATUS) 0xC0000719L) +#endif + +#ifndef STATUS_PORT_ALREADY_HAS_COMPLETION_LIST +# define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST ((NTSTATUS) 0xC000071AL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_THREAD_PRIORITY +# define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY ((NTSTATUS) 0xC000071BL) +#endif + +#ifndef STATUS_INVALID_THREAD +# define STATUS_INVALID_THREAD ((NTSTATUS) 0xC000071CL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_TRANSACTION +# define STATUS_CALLBACK_RETURNED_TRANSACTION ((NTSTATUS) 0xC000071DL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_LDR_LOCK +# define STATUS_CALLBACK_RETURNED_LDR_LOCK ((NTSTATUS) 0xC000071EL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_LANG +# define STATUS_CALLBACK_RETURNED_LANG ((NTSTATUS) 0xC000071FL) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_PRI_BACK +# define STATUS_CALLBACK_RETURNED_PRI_BACK ((NTSTATUS) 0xC0000720L) +#endif + +#ifndef STATUS_CALLBACK_RETURNED_THREAD_AFFINITY +# define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY ((NTSTATUS) 0xC0000721L) +#endif + +#ifndef STATUS_DISK_REPAIR_DISABLED +# define STATUS_DISK_REPAIR_DISABLED ((NTSTATUS) 0xC0000800L) +#endif + +#ifndef STATUS_DS_DOMAIN_RENAME_IN_PROGRESS +# define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS ((NTSTATUS) 0xC0000801L) +#endif + +#ifndef STATUS_DISK_QUOTA_EXCEEDED +# define STATUS_DISK_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000802L) +#endif + +#ifndef STATUS_DATA_LOST_REPAIR +# define STATUS_DATA_LOST_REPAIR ((NTSTATUS) 0x80000803L) +#endif + +#ifndef STATUS_CONTENT_BLOCKED +# define STATUS_CONTENT_BLOCKED ((NTSTATUS) 0xC0000804L) +#endif + +#ifndef STATUS_BAD_CLUSTERS +# define STATUS_BAD_CLUSTERS ((NTSTATUS) 0xC0000805L) +#endif + +#ifndef STATUS_VOLUME_DIRTY +# define STATUS_VOLUME_DIRTY ((NTSTATUS) 0xC0000806L) +#endif + +#ifndef STATUS_FILE_CHECKED_OUT +# define STATUS_FILE_CHECKED_OUT ((NTSTATUS) 0xC0000901L) +#endif + +#ifndef STATUS_CHECKOUT_REQUIRED +# define STATUS_CHECKOUT_REQUIRED ((NTSTATUS) 0xC0000902L) +#endif + +#ifndef STATUS_BAD_FILE_TYPE +# define STATUS_BAD_FILE_TYPE ((NTSTATUS) 0xC0000903L) +#endif + +#ifndef STATUS_FILE_TOO_LARGE +# define STATUS_FILE_TOO_LARGE ((NTSTATUS) 0xC0000904L) +#endif + +#ifndef STATUS_FORMS_AUTH_REQUIRED +# define STATUS_FORMS_AUTH_REQUIRED ((NTSTATUS) 0xC0000905L) +#endif + +#ifndef STATUS_VIRUS_INFECTED +# define STATUS_VIRUS_INFECTED ((NTSTATUS) 0xC0000906L) +#endif + +#ifndef STATUS_VIRUS_DELETED +# define STATUS_VIRUS_DELETED ((NTSTATUS) 0xC0000907L) +#endif + +#ifndef STATUS_BAD_MCFG_TABLE +# define STATUS_BAD_MCFG_TABLE ((NTSTATUS) 0xC0000908L) +#endif + +#ifndef STATUS_CANNOT_BREAK_OPLOCK +# define STATUS_CANNOT_BREAK_OPLOCK ((NTSTATUS) 0xC0000909L) +#endif + +#ifndef STATUS_WOW_ASSERTION +# define STATUS_WOW_ASSERTION ((NTSTATUS) 0xC0009898L) +#endif + +#ifndef STATUS_INVALID_SIGNATURE +# define STATUS_INVALID_SIGNATURE ((NTSTATUS) 0xC000A000L) +#endif + +#ifndef STATUS_HMAC_NOT_SUPPORTED +# define STATUS_HMAC_NOT_SUPPORTED ((NTSTATUS) 0xC000A001L) +#endif + +#ifndef STATUS_AUTH_TAG_MISMATCH +# define STATUS_AUTH_TAG_MISMATCH ((NTSTATUS) 0xC000A002L) +#endif + +#ifndef STATUS_IPSEC_QUEUE_OVERFLOW +# define STATUS_IPSEC_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A010L) +#endif + +#ifndef STATUS_ND_QUEUE_OVERFLOW +# define STATUS_ND_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A011L) +#endif + +#ifndef STATUS_HOPLIMIT_EXCEEDED +# define STATUS_HOPLIMIT_EXCEEDED ((NTSTATUS) 0xC000A012L) +#endif + +#ifndef STATUS_PROTOCOL_NOT_SUPPORTED +# define STATUS_PROTOCOL_NOT_SUPPORTED ((NTSTATUS) 0xC000A013L) +#endif + +#ifndef STATUS_FASTPATH_REJECTED +# define STATUS_FASTPATH_REJECTED ((NTSTATUS) 0xC000A014L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED +# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED ((NTSTATUS) 0xC000A080L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR +# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR ((NTSTATUS) 0xC000A081L) +#endif + +#ifndef STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR +# define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR ((NTSTATUS) 0xC000A082L) +#endif + +#ifndef STATUS_XML_PARSE_ERROR +# define STATUS_XML_PARSE_ERROR ((NTSTATUS) 0xC000A083L) +#endif + +#ifndef STATUS_XMLDSIG_ERROR +# define STATUS_XMLDSIG_ERROR ((NTSTATUS) 0xC000A084L) +#endif + +#ifndef STATUS_WRONG_COMPARTMENT +# define STATUS_WRONG_COMPARTMENT ((NTSTATUS) 0xC000A085L) +#endif + +#ifndef STATUS_AUTHIP_FAILURE +# define STATUS_AUTHIP_FAILURE ((NTSTATUS) 0xC000A086L) +#endif + +#ifndef STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS +# define STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS ((NTSTATUS) 0xC000A087L) +#endif + +#ifndef STATUS_DS_OID_NOT_FOUND +# define STATUS_DS_OID_NOT_FOUND ((NTSTATUS) 0xC000A088L) +#endif + +#ifndef STATUS_HASH_NOT_SUPPORTED +# define STATUS_HASH_NOT_SUPPORTED ((NTSTATUS) 0xC000A100L) +#endif + +#ifndef STATUS_HASH_NOT_PRESENT +# define STATUS_HASH_NOT_PRESENT ((NTSTATUS) 0xC000A101L) +#endif + +/* This is not the NTSTATUS_FROM_WIN32 that the DDK provides, because the +/* DDK got it wrong! */ +#ifdef NTSTATUS_FROM_WIN32 +# undef NTSTATUS_FROM_WIN32 +#endif +#define NTSTATUS_FROM_WIN32(error) ((NTSTATUS) (error) <= 0 ? \ + ((NTSTATUS) (error)) : ((NTSTATUS) (((error) & 0x0000FFFF) | \ + (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_WARNING))) + +/* from ntifs.h */ +/* MinGW already has it */ +#ifndef __MINGW32__ + typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; + } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +#endif + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + DWORD FileAttributes; +} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; + +typedef enum _FILE_INFORMATION_CLASS { + FileDirectoryInformation = 1, + FileFullDirectoryInformation, + FileBothDirectoryInformation, + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, + FileLinkInformation, + FileNamesInformation, + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, + FileCompletionInformation, + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, + FileIdFullDirectoryInformation, + FileValidDataLengthInformation, + FileShortNameInformation, + FileIoCompletionNotificationInformation, + FileIoStatusBlockRangeInformation, + FileIoPriorityHintInformation, + FileSfioReserveInformation, + FileSfioVolumeInformation, + FileHardLinkInformation, + FileProcessIdsUsingFileInformation, + FileNormalizedNameInformation, + FileNetworkPhysicalNameInformation, + FileIdGlobalTxDirectoryInformation, + FileIsRemoteDeviceInformation, + FileAttributeCacheInformation, + FileNumaNodeInformation, + FileStandardLinkInformation, + FileRemoteProtocolInformation, + FileMaximumInformation +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +#ifndef DEVICE_TYPE +# define DEVICE_TYPE DWORD +#endif + +#ifndef FILE_DEVICE_FILE_SYSTEM +# define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#endif + +#ifndef METHOD_BUFFERED +# define METHOD_BUFFERED 0 +#endif + +#ifndef METHOD_IN_DIRECT +# define METHOD_IN_DIRECT 1 +#endif + +#ifndef METHOD_OUT_DIRECT +# define METHOD_OUT_DIRECT 2 +#endif + +#ifndef METHOD_NEITHER +#define METHOD_NEITHER 3 +#endif + +#ifndef METHOD_DIRECT_TO_HARDWARE +# define METHOD_DIRECT_TO_HARDWARE METHOD_IN_DIRECT +#endif + +#ifndef METHOD_DIRECT_FROM_HARDWARE +# define METHOD_DIRECT_FROM_HARDWARE METHOD_OUT_DIRECT +#endif + +#ifndef FILE_ANY_ACCESS +# define FILE_ANY_ACCESS 0 +#endif + +#ifndef FILE_SPECIAL_ACCESS +# define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) +#endif + +#ifndef FILE_READ_ACCESS +# define FILE_READ_ACCESS 0x0001 +#endif + +#ifndef FILE_WRITE_ACCESS +# define FILE_WRITE_ACCESS 0x0002 +#endif + +#ifndef CTL_CODE +# define CTL_CODE(device_type, function, method, access) \ + (((device_type) << 16) | ((access) << 14) | ((function) << 2) | (method)) +#endif + +#ifndef FSCTL_SET_REPARSE_POINT +# define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ + 41, \ + METHOD_BUFFERED, \ + FILE_SPECIAL_ACCESS) +#endif + +#ifndef FSCTL_GET_REPARSE_POINT +# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ + 42, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) +#endif + +#ifndef FSCTL_DELETE_REPARSE_POINT +# define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, \ + 43, \ + METHOD_BUFFERED, \ + FILE_SPECIAL_ACCESS) +#endif + +typedef ULONG (NTAPI *sRtlNtStatusToDosError) + (NTSTATUS Status); + +typedef NTSTATUS (NTAPI *sNtQueryInformationFile) + (HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +typedef NTSTATUS (NTAPI *sNtSetInformationFile) + (HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + + +/* + * Kernel32 headers + */ +#define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 +#define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 + +#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 + +#ifdef __MINGW32__ + typedef struct _OVERLAPPED_ENTRY { + ULONG_PTR lpCompletionKey; + LPOVERLAPPED lpOverlapped; + ULONG_PTR Internal; + DWORD dwNumberOfBytesTransferred; + } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; +#endif + +typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx) + (HANDLE CompletionPort, + LPOVERLAPPED_ENTRY lpCompletionPortEntries, + ULONG ulCount, + PULONG ulNumEntriesRemoved, + DWORD dwMilliseconds, + BOOL fAlertable); + +typedef BOOL (WINAPI* sSetFileCompletionNotificationModes) + (HANDLE FileHandle, + UCHAR Flags); + +typedef BOOLEAN (WINAPI* sCreateSymbolicLinkA) + (LPCSTR lpSymlinkFileName, + LPCSTR lpTargetFileName, + DWORD dwFlags); + + +/* Ntapi function pointers */ +extern sRtlNtStatusToDosError pRtlNtStatusToDosError; +extern sNtQueryInformationFile pNtQueryInformationFile; +extern sNtSetInformationFile pNtSetInformationFile; + + +/* Kernel32 function pointers */ +extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; +extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; +extern sCreateSymbolicLinkA pCreateSymbolicLinkA; + +#endif /* UV_WIN_WINAPI_H_ */ diff --git a/src/rt/libuv/src/win/winsock.c b/src/rt/libuv/src/win/winsock.c new file mode 100644 index 00000000000..1f56b3d75bd --- /dev/null +++ b/src/rt/libuv/src/win/winsock.c @@ -0,0 +1,270 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* Winsock extension functions (ipv4) */ +LPFN_CONNECTEX pConnectEx; +LPFN_ACCEPTEX pAcceptEx; +LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; +LPFN_DISCONNECTEX pDisconnectEx; +LPFN_TRANSMITFILE pTransmitFile; + +/* Winsock extension functions (ipv6) */ +LPFN_CONNECTEX pConnectEx6; +LPFN_ACCEPTEX pAcceptEx6; +LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; +LPFN_DISCONNECTEX pDisconnectEx6; +LPFN_TRANSMITFILE pTransmitFile6; + +/* Whether ipv6 is supported */ +int uv_allow_ipv6; + +/* Ip address used to bind to any port at any interface */ +struct sockaddr_in uv_addr_ip4_any_; +struct sockaddr_in6 uv_addr_ip6_any_; + + +/* + * Retrieves the pointer to a winsock extension function. + */ +static BOOL uv_get_extension_function(SOCKET socket, GUID guid, + void **target) { + DWORD result, bytes; + + result = WSAIoctl(socket, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, + sizeof(guid), + (void*)target, + sizeof(*target), + &bytes, + NULL, + NULL); + + if (result == SOCKET_ERROR) { + *target = NULL; + return FALSE; + } else { + return TRUE; + } +} + + +void uv_winsock_init() { + const GUID wsaid_connectex = WSAID_CONNECTEX; + const GUID wsaid_acceptex = WSAID_ACCEPTEX; + const GUID wsaid_getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; + const GUID wsaid_disconnectex = WSAID_DISCONNECTEX; + const GUID wsaid_transmitfile = WSAID_TRANSMITFILE; + + WSADATA wsa_data; + int errorno; + SOCKET dummy; + SOCKET dummy6; + + /* Initialize winsock */ + errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (errorno != 0) { + uv_fatal_error(errorno, "WSAStartup"); + } + + /* Set implicit binding address used by connectEx */ + uv_addr_ip4_any_ = uv_ip4_addr("0.0.0.0", 0); + uv_addr_ip6_any_ = uv_ip6_addr("::", 0); + + /* Retrieve the needed winsock extension function pointers. */ + dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (dummy == INVALID_SOCKET) { + uv_fatal_error(WSAGetLastError(), "socket"); + } + + if (!uv_get_extension_function(dummy, + wsaid_connectex, + (void**)&pConnectEx) || + !uv_get_extension_function(dummy, + wsaid_acceptex, + (void**)&pAcceptEx) || + !uv_get_extension_function(dummy, + wsaid_getacceptexsockaddrs, + (void**)&pGetAcceptExSockAddrs) || + !uv_get_extension_function(dummy, + wsaid_disconnectex, + (void**)&pDisconnectEx) || + !uv_get_extension_function(dummy, + wsaid_transmitfile, + (void**)&pTransmitFile)) { + uv_fatal_error(WSAGetLastError(), + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); + } + + if (closesocket(dummy) == SOCKET_ERROR) { + uv_fatal_error(WSAGetLastError(), "closesocket"); + } + + /* optional IPv6 versions of winsock extension functions */ + dummy6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); + if (dummy6 != INVALID_SOCKET) { + uv_allow_ipv6 = TRUE; + + if (!uv_get_extension_function(dummy6, + wsaid_connectex, + (void**)&pConnectEx6) || + !uv_get_extension_function(dummy6, + wsaid_acceptex, + (void**)&pAcceptEx6) || + !uv_get_extension_function(dummy6, + wsaid_getacceptexsockaddrs, + (void**)&pGetAcceptExSockAddrs6) || + !uv_get_extension_function(dummy6, + wsaid_disconnectex, + (void**)&pDisconnectEx6) || + !uv_get_extension_function(dummy6, + wsaid_transmitfile, + (void**)&pTransmitFile6)) { + uv_allow_ipv6 = FALSE; + } + + if (closesocket(dummy6) == SOCKET_ERROR) { + uv_fatal_error(WSAGetLastError(), "closesocket"); + } + } +} + + +int uv_ntstatus_to_winsock_error(NTSTATUS status) { + switch (status) { + case STATUS_SUCCESS: + return ERROR_SUCCESS; + + case STATUS_PENDING: + return ERROR_IO_PENDING; + + case STATUS_INVALID_HANDLE: + case STATUS_OBJECT_TYPE_MISMATCH: + return WSAENOTSOCK; + + case STATUS_INSUFFICIENT_RESOURCES: + case STATUS_PAGEFILE_QUOTA: + case STATUS_COMMITMENT_LIMIT: + case STATUS_WORKING_SET_QUOTA: + case STATUS_NO_MEMORY: + case STATUS_CONFLICTING_ADDRESSES: + case STATUS_QUOTA_EXCEEDED: + case STATUS_TOO_MANY_PAGING_FILES: + case STATUS_REMOTE_RESOURCES: + case STATUS_TOO_MANY_ADDRESSES: + return WSAENOBUFS; + + case STATUS_SHARING_VIOLATION: + case STATUS_ADDRESS_ALREADY_EXISTS: + return WSAEADDRINUSE; + + case STATUS_LINK_TIMEOUT: + case STATUS_IO_TIMEOUT: + case STATUS_TIMEOUT: + return WSAETIMEDOUT; + + case STATUS_GRACEFUL_DISCONNECT: + return WSAEDISCON; + + case STATUS_REMOTE_DISCONNECT: + case STATUS_CONNECTION_RESET: + case STATUS_LINK_FAILED: + case STATUS_CONNECTION_DISCONNECTED: + case STATUS_PORT_UNREACHABLE: + return WSAECONNRESET; + + case STATUS_LOCAL_DISCONNECT: + case STATUS_TRANSACTION_ABORTED: + case STATUS_CONNECTION_ABORTED: + return WSAECONNABORTED; + + case STATUS_BAD_NETWORK_PATH: + case STATUS_NETWORK_UNREACHABLE: + case STATUS_PROTOCOL_UNREACHABLE: + return WSAENETUNREACH; + + case STATUS_HOST_UNREACHABLE: + return WSAEHOSTUNREACH; + + case STATUS_CANCELLED: + case STATUS_REQUEST_ABORTED: + return WSAEINTR; + + case STATUS_BUFFER_OVERFLOW: + case STATUS_INVALID_BUFFER_SIZE: + return WSAEMSGSIZE; + + case STATUS_BUFFER_TOO_SMALL: + case STATUS_ACCESS_VIOLATION: + return WSAEFAULT; + + case STATUS_DEVICE_NOT_READY: + case STATUS_REQUEST_NOT_ACCEPTED: + return WSAEWOULDBLOCK; + + case STATUS_INVALID_NETWORK_RESPONSE: + case STATUS_NETWORK_BUSY: + case STATUS_NO_SUCH_DEVICE: + case STATUS_NO_SUCH_FILE: + case STATUS_OBJECT_PATH_NOT_FOUND: + case STATUS_OBJECT_NAME_NOT_FOUND: + case STATUS_UNEXPECTED_NETWORK_ERROR: + return WSAENETDOWN; + + case STATUS_INVALID_CONNECTION: + return WSAENOTCONN; + + case STATUS_REMOTE_NOT_LISTENING: + case STATUS_CONNECTION_REFUSED: + return WSAECONNREFUSED; + + case STATUS_PIPE_DISCONNECTED: + return WSAESHUTDOWN; + + case STATUS_INVALID_ADDRESS: + case STATUS_INVALID_ADDRESS_COMPONENT: + return WSAEADDRNOTAVAIL; + + case STATUS_NOT_SUPPORTED: + case STATUS_NOT_IMPLEMENTED: + return WSAEOPNOTSUPP; + + case STATUS_ACCESS_DENIED: + return WSAEACCES; + + default: + if (status & ((FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_ERROR)) { + /* It's a windows error that has been previously mapped to an */ + /* ntstatus code. */ + return (DWORD) (status & 0xffff); + } else { + /* The default fallback for unmappable ntstatus codes. */ + return WSAEINVAL; + } + } +} diff --git a/src/rt/libuv/src/win/winsock.h b/src/rt/libuv/src/win/winsock.h new file mode 100644 index 00000000000..2c9fb92db1b --- /dev/null +++ b/src/rt/libuv/src/win/winsock.h @@ -0,0 +1,134 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_WIN_WINSOCK_H_ +#define UV_WIN_WINSOCK_H_ + +#include +#include +#include +#include + + +/* + * Guids and typedefs for winsock extension functions + * Mingw32 doesn't have these :-( + */ +#ifndef WSAID_ACCEPTEX +# define WSAID_ACCEPTEX \ + {0xb5367df1, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_CONNECTEX \ + {0x25a207b9, 0xddf3, 0x4660, \ + {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} + +# define WSAID_GETACCEPTEXSOCKADDRS \ + {0xb5367df2, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_DISCONNECTEX \ + {0x7fda2e11, 0x8630, 0x436f, \ + {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} + +# define WSAID_TRANSMITFILE \ + {0xb5367df0, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + + typedef BOOL PASCAL (*LPFN_ACCEPTEX) + (SOCKET sListenSocket, + SOCKET sAcceptSocket, + PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPDWORD lpdwBytesReceived, + LPOVERLAPPED lpOverlapped); + + typedef BOOL PASCAL (*LPFN_CONNECTEX) + (SOCKET s, + const struct sockaddr* name, + int namelen, + PVOID lpSendBuffer, + DWORD dwSendDataLength, + LPDWORD lpdwBytesSent, + LPOVERLAPPED lpOverlapped); + + typedef void PASCAL (*LPFN_GETACCEPTEXSOCKADDRS) + (PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPSOCKADDR* LocalSockaddr, + LPINT LocalSockaddrLength, + LPSOCKADDR* RemoteSockaddr, + LPINT RemoteSockaddrLength); + + typedef BOOL PASCAL (*LPFN_DISCONNECTEX) + (SOCKET hSocket, + LPOVERLAPPED lpOverlapped, + DWORD dwFlags, + DWORD reserved); + + typedef BOOL PASCAL (*LPFN_TRANSMITFILE) + (SOCKET hSocket, + HANDLE hFile, + DWORD nNumberOfBytesToWrite, + DWORD nNumberOfBytesPerSend, + LPOVERLAPPED lpOverlapped, + LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, + DWORD dwFlags); +#endif + +/* + * MinGW is missing these too + */ +#ifndef SO_UPDATE_CONNECT_CONTEXT +# define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + +#ifndef IPV6_V6ONLY + #define IPV6_V6ONLY 27 +#endif + + +/* Winsock extension functions (ipv4) */ +extern LPFN_CONNECTEX pConnectEx; +extern LPFN_ACCEPTEX pAcceptEx; +extern LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; +extern LPFN_DISCONNECTEX pDisconnectEx; +extern LPFN_TRANSMITFILE pTransmitFile; + +/* Winsock extension functions (ipv6) */ +extern LPFN_CONNECTEX pConnectEx6; +extern LPFN_ACCEPTEX pAcceptEx6; +extern LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; +extern LPFN_DISCONNECTEX pDisconnectEx6; +extern LPFN_TRANSMITFILE pTransmitFile6; + +/* Whether ipv6 is supported */ +extern int uv_allow_ipv6; + +/* Ip address used to bind to any port at any interface */ +extern struct sockaddr_in uv_addr_ip4_any_; +extern struct sockaddr_in6 uv_addr_ip6_any_; + +#endif /* UV_WIN_WINSOCK_H_ */ diff --git a/src/rt/libuv/test/benchmark-ares.c b/src/rt/libuv/test/benchmark-ares.c index c3f538bff95..bfd081deff5 100644 --- a/src/rt/libuv/test/benchmark-ares.c +++ b/src/rt/libuv/test/benchmark-ares.c @@ -26,6 +26,8 @@ #include #include /* strlen */ +static uv_loop_t* loop; + ares_channel channel; struct ares_options options; int optmask; @@ -67,7 +69,7 @@ static void prep_tcploopback() options.tcp_port = htons(TEST_PORT_2); options.flags = ARES_FLAG_USEVC; - rc = uv_ares_init_options(&channel, &options, optmask); + rc = uv_ares_init_options(loop, &channel, &options, optmask); ASSERT(rc == ARES_SUCCESS); } @@ -84,12 +86,13 @@ BENCHMARK_IMPL(gethostbyname) { return 1; } - uv_init(); + loop = uv_default_loop(); + ares_callbacks = 0; ares_errors = 0; - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); prep_tcploopback(); @@ -101,16 +104,17 @@ BENCHMARK_IMPL(gethostbyname) { &argument); } - uv_run(); + uv_run(loop); - uv_ares_destroy(channel); + uv_ares_destroy(loop, channel); - end_time = uv_now(); + end_time = uv_now(loop); if (ares_errors > 0) { printf("There were %d failures\n", ares_errors); } - LOGF("ares_gethostbyname: %d calls in %d ms \n", ares_callbacks, (int) (end_time - start_time)); + LOGF("ares_gethostbyname: %.0f req/s\n", + 1000.0 * ares_callbacks / (double)(end_time - start_time)); return 0; } diff --git a/src/rt/libuv/test/benchmark-getaddrinfo.c b/src/rt/libuv/test/benchmark-getaddrinfo.c index ba859c3c82e..892c14d1e95 100644 --- a/src/rt/libuv/test/benchmark-getaddrinfo.c +++ b/src/rt/libuv/test/benchmark-getaddrinfo.c @@ -32,6 +32,8 @@ const char* name = "localhost"; +static uv_loop_t* loop; + static uv_getaddrinfo_t handles[CONCURRENT_CALLS]; static int calls_initiated = 0; @@ -50,6 +52,8 @@ static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, if (calls_initiated < TOTAL_CALLS) { getaddrinfo_initiate(handle); } + + uv_freeaddrinfo(res); } @@ -58,7 +62,7 @@ static void getaddrinfo_initiate(uv_getaddrinfo_t* handle) { calls_initiated++; - r = uv_getaddrinfo(handle, &getaddrinfo_cb, name, NULL, NULL); + r = uv_getaddrinfo(loop, handle, &getaddrinfo_cb, name, NULL, NULL); ASSERT(r == 0); } @@ -66,26 +70,24 @@ static void getaddrinfo_initiate(uv_getaddrinfo_t* handle) { BENCHMARK_IMPL(getaddrinfo) { int i; - uv_init(); + loop = uv_default_loop(); - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); for (i = 0; i < CONCURRENT_CALLS; i++) { getaddrinfo_initiate(&handles[i]); } - uv_run(); + uv_run(loop); - uv_update_time(); - end_time = uv_now(); + uv_update_time(loop); + end_time = uv_now(loop); ASSERT(calls_initiated == TOTAL_CALLS); ASSERT(calls_completed == TOTAL_CALLS); - LOGF("getaddrinfo: %d calls in %d ms (%.0f requests/second)\n", - calls_completed, - (int) (end_time - start_time), + LOGF("getaddrinfo: %.0f req/s\n", (double) calls_completed / (double) (end_time - start_time) * 1000.0); return 0; diff --git a/src/rt/libuv/test/benchmark-list.h b/src/rt/libuv/test/benchmark-list.h index 6040e90a326..0e5467c7a1b 100644 --- a/src/rt/libuv/test/benchmark-list.h +++ b/src/rt/libuv/test/benchmark-list.h @@ -21,28 +21,78 @@ BENCHMARK_DECLARE (sizes) BENCHMARK_DECLARE (ping_pongs) -BENCHMARK_DECLARE (pump100_client) -BENCHMARK_DECLARE (pump1_client) +BENCHMARK_DECLARE (tcp4_pound_100) +BENCHMARK_DECLARE (tcp4_pound_1000) +BENCHMARK_DECLARE (pipe_pound_100) +BENCHMARK_DECLARE (pipe_pound_1000) +BENCHMARK_DECLARE (tcp_pump100_client) +BENCHMARK_DECLARE (tcp_pump1_client) +BENCHMARK_DECLARE (pipe_pump100_client) +BENCHMARK_DECLARE (pipe_pump1_client) +BENCHMARK_DECLARE (udp_packet_storm_1v1) +BENCHMARK_DECLARE (udp_packet_storm_1v10) +BENCHMARK_DECLARE (udp_packet_storm_1v100) +BENCHMARK_DECLARE (udp_packet_storm_1v1000) +BENCHMARK_DECLARE (udp_packet_storm_10v10) +BENCHMARK_DECLARE (udp_packet_storm_10v100) +BENCHMARK_DECLARE (udp_packet_storm_10v1000) +BENCHMARK_DECLARE (udp_packet_storm_100v100) +BENCHMARK_DECLARE (udp_packet_storm_100v1000) +BENCHMARK_DECLARE (udp_packet_storm_1000v1000) BENCHMARK_DECLARE (gethostbyname) BENCHMARK_DECLARE (getaddrinfo) -HELPER_DECLARE (pump_server) -HELPER_DECLARE (echo_server) +BENCHMARK_DECLARE (spawn) +HELPER_DECLARE (tcp_pump_server) +HELPER_DECLARE (pipe_pump_server) +HELPER_DECLARE (tcp4_echo_server) +HELPER_DECLARE (pipe_echo_server) HELPER_DECLARE (dns_server) TASK_LIST_START BENCHMARK_ENTRY (sizes) BENCHMARK_ENTRY (ping_pongs) - BENCHMARK_HELPER (ping_pongs, echo_server) + BENCHMARK_HELPER (ping_pongs, tcp4_echo_server) - BENCHMARK_ENTRY (pump100_client) - BENCHMARK_HELPER (pump100_client, pump_server) + BENCHMARK_ENTRY (tcp_pump100_client) + BENCHMARK_HELPER (tcp_pump100_client, tcp_pump_server) - BENCHMARK_ENTRY (pump1_client) - BENCHMARK_HELPER (pump1_client, pump_server) + BENCHMARK_ENTRY (tcp_pump1_client) + BENCHMARK_HELPER (tcp_pump1_client, tcp_pump_server) + + BENCHMARK_ENTRY (tcp4_pound_100) + BENCHMARK_HELPER (tcp4_pound_100, tcp4_echo_server) + + BENCHMARK_ENTRY (tcp4_pound_1000) + BENCHMARK_HELPER (tcp4_pound_1000, tcp4_echo_server) + + BENCHMARK_ENTRY (pipe_pump100_client) + BENCHMARK_HELPER (pipe_pump100_client, pipe_pump_server) + + BENCHMARK_ENTRY (pipe_pump1_client) + BENCHMARK_HELPER (pipe_pump1_client, pipe_pump_server) + + BENCHMARK_ENTRY (pipe_pound_100) + BENCHMARK_HELPER (pipe_pound_100, pipe_echo_server) + + BENCHMARK_ENTRY (pipe_pound_1000) + BENCHMARK_HELPER (pipe_pound_1000, pipe_echo_server) + + BENCHMARK_ENTRY (udp_packet_storm_1v1) + BENCHMARK_ENTRY (udp_packet_storm_1v10) + BENCHMARK_ENTRY (udp_packet_storm_1v100) + BENCHMARK_ENTRY (udp_packet_storm_1v1000) + BENCHMARK_ENTRY (udp_packet_storm_10v10) + BENCHMARK_ENTRY (udp_packet_storm_10v100) + BENCHMARK_ENTRY (udp_packet_storm_10v1000) + BENCHMARK_ENTRY (udp_packet_storm_100v100) + BENCHMARK_ENTRY (udp_packet_storm_100v1000) + BENCHMARK_ENTRY (udp_packet_storm_1000v1000) BENCHMARK_ENTRY (gethostbyname) BENCHMARK_HELPER (gethostbyname, dns_server) BENCHMARK_ENTRY (getaddrinfo) + + BENCHMARK_ENTRY (spawn) TASK_LIST_END diff --git a/src/rt/libuv/test/benchmark-ping-pongs.c b/src/rt/libuv/test/benchmark-ping-pongs.c index 7124a3614a4..c36215c1deb 100644 --- a/src/rt/libuv/test/benchmark-ping-pongs.c +++ b/src/rt/libuv/test/benchmark-ping-pongs.c @@ -34,8 +34,8 @@ typedef struct { int pongs; int state; uv_tcp_t tcp; - uv_req_t connect_req; - uv_req_t shutdown_req; + uv_connect_t connect_req; + uv_shutdown_t shutdown_req; } pinger_t; typedef struct buf_s { @@ -46,13 +46,15 @@ typedef struct buf_s { static char PING[] = "PING\n"; +static uv_loop_t* loop; + static buf_t* buf_freelist = NULL; static int pinger_shutdown_cb_called; static int completed_pingers = 0; static int64_t start_time; -static uv_buf_t buf_alloc(uv_stream_t* tcp, size_t size) { +static uv_buf_t buf_alloc(uv_handle_t* tcp, size_t size) { buf_t* ab; ab = buf_freelist; @@ -90,7 +92,7 @@ static void pinger_close_cb(uv_handle_t* handle) { } -static void pinger_write_cb(uv_req_t *req, int status) { +static void pinger_write_cb(uv_write_t* req, int status) { ASSERT(status == 0); free(req); @@ -98,22 +100,20 @@ static void pinger_write_cb(uv_req_t *req, int status) { static void pinger_write_ping(pinger_t* pinger) { - uv_req_t *req; + uv_write_t* req; uv_buf_t buf; buf.base = (char*)&PING; buf.len = strlen(PING); - req = (uv_req_t*)malloc(sizeof(*req)); - uv_req_init(req, (uv_handle_t*)(&pinger->tcp), pinger_write_cb); - - if (uv_write(req, &buf, 1)) { + req = malloc(sizeof *req); + if (uv_write(req, (uv_stream_t*) &pinger->tcp, &buf, 1, pinger_write_cb)) { FATAL("uv_write failed"); } } -static void pinger_shutdown_cb(uv_handle_t* handle, int status) { +static void pinger_shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(status == 0); pinger_shutdown_cb_called++; @@ -126,13 +126,13 @@ static void pinger_shutdown_cb(uv_handle_t* handle, int status) { static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { - unsigned int i; + ssize_t i; pinger_t* pinger; pinger = (pinger_t*)tcp->data; if (nread < 0) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(loop).code == UV_EOF); if (buf.base) { buf_free(buf); @@ -150,9 +150,8 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); if (pinger->state == 0) { pinger->pongs++; - if (uv_now() - start_time > TIME) { - uv_req_init(&pinger->shutdown_req, (uv_handle_t*)tcp, pinger_shutdown_cb); - uv_shutdown(&pinger->shutdown_req); + if (uv_now(loop) - start_time > TIME) { + uv_shutdown(&pinger->shutdown_req, (uv_stream_t*) tcp, pinger_shutdown_cb); break; } else { pinger_write_ping(pinger); @@ -164,14 +163,14 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { } -static void pinger_connect_cb(uv_req_t *req, int status) { +static void pinger_connect_cb(uv_connect_t* req, int status) { pinger_t *pinger = (pinger_t*)req->handle->data; ASSERT(status == 0); pinger_write_ping(pinger); - if (uv_read_start((uv_stream_t*)(req->handle), buf_alloc, pinger_read_cb)) { + if (uv_read_start(req->handle, buf_alloc, pinger_read_cb)) { FATAL("uv_read_start failed"); } } @@ -188,28 +187,25 @@ static void pinger_new() { pinger->pongs = 0; /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&pinger->tcp); + r = uv_tcp_init(loop, &pinger->tcp); ASSERT(!r); pinger->tcp.data = pinger; - /* We are never doing multiple reads/connects at a time anyway. */ - /* so these handles can be pre-initialized. */ - uv_req_init(&pinger->connect_req, (uv_handle_t*)&pinger->tcp, - pinger_connect_cb); - uv_tcp_bind(&pinger->tcp, client_addr); - r = uv_tcp_connect(&pinger->connect_req, server_addr); + + r = uv_tcp_connect(&pinger->connect_req, &pinger->tcp, server_addr, pinger_connect_cb); ASSERT(!r); } BENCHMARK_IMPL(ping_pongs) { - uv_init(); - start_time = uv_now(); + loop = uv_default_loop(); + + start_time = uv_now(loop); pinger_new(); - uv_run(); + uv_run(loop); ASSERT(completed_pingers == 1); diff --git a/src/rt/libuv/test/benchmark-pound.c b/src/rt/libuv/test/benchmark-pound.c new file mode 100644 index 00000000000..1f56e27f69d --- /dev/null +++ b/src/rt/libuv/test/benchmark-pound.c @@ -0,0 +1,327 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "task.h" +#include "uv.h" + +/* Update this is you're going to run > 1000 concurrent requests. */ +#define MAX_CONNS 1000 + +#undef NANOSEC +#define NANOSEC ((uint64_t)10e8) + +#define DEBUG 0 + +struct conn_rec_s; + +typedef void (*setup_fn)(int num, void* arg); +typedef void (*make_connect_fn)(struct conn_rec_s* conn); +typedef int (*connect_fn)(int num, make_connect_fn make_connect, void* arg); + +/* Base class for tcp_conn_rec and pipe_conn_rec. + * The ordering of fields matters! + */ +typedef struct conn_rec_s { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_stream_t stream; +} conn_rec; + +typedef struct { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_tcp_t stream; +} tcp_conn_rec; + +typedef struct { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_pipe_t stream; +} pipe_conn_rec; + +static char buffer[] = "QS"; + +static uv_loop_t* loop; + +static tcp_conn_rec tcp_conns[MAX_CONNS]; +static pipe_conn_rec pipe_conns[MAX_CONNS]; + +static uint64_t start; /* in ms */ +static int closed_streams; +static int conns_failed; + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size); +static void connect_cb(uv_connect_t* conn_req, int status); +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); +static void close_cb(uv_handle_t* handle); + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + uv_buf_t buf; + buf.base = slab; + buf.len = sizeof(slab); + return buf; +} + + +static void after_write(uv_write_t* req, int status) { + if (status != 0) { + fprintf(stderr, "write error %s\n", uv_err_name(uv_last_error(loop))); + uv_close((uv_handle_t*)req->handle, close_cb); + conns_failed++; + return; + } +} + + +static void connect_cb(uv_connect_t* req, int status) { + conn_rec* conn; + uv_buf_t buf; + int r; + + if (status != 0) { +#if DEBUG + fprintf(stderr, "connect error %s\n", uv_err_name(uv_last_error())); +#endif + uv_close((uv_handle_t*)req->handle, close_cb); + conns_failed++; + return; + } + + ASSERT(req != NULL); + ASSERT(status == 0); + + conn = (conn_rec*)req->data; + ASSERT(conn != NULL); + +#if DEBUG + printf("connect_cb %d\n", conn->i); +#endif + + r = uv_read_start(&conn->stream, alloc_cb, read_cb); + ASSERT(r == 0); + + buf.base = buffer; + buf.len = sizeof(buffer) - 1; + + r = uv_write(&conn->write_req, &conn->stream, &buf, 1, after_write); + ASSERT(r == 0); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { + conn_rec* p = (conn_rec*)stream->data; + uv_err_t err = uv_last_error(loop); + + ASSERT(stream != NULL); + +#if DEBUG + printf("read_cb %d\n", p->i); +#endif + + uv_close((uv_handle_t*)stream, close_cb); + + if (nread == -1) { + if (err.code == UV_EOF) { + ; + } else if (err.code == UV_ECONNRESET) { + conns_failed++; + } else { + fprintf(stderr, "read error %s\n", uv_err_name(uv_last_error(loop))); + ASSERT(0); + } + } +} + + +static void close_cb(uv_handle_t* handle) { + conn_rec* p = (conn_rec*)handle->data; + + ASSERT(handle != NULL); + closed_streams++; + +#if DEBUG + printf("close_cb %d\n", p->i); +#endif + + if (uv_now(loop) - start < 10000) { + p->make_connect(p); + } +} + + +static void tcp_do_setup(int num, void* arg) { + int i; + + for (i = 0; i < num; i++) { + tcp_conns[i].i = i; + } +} + + +static void pipe_do_setup(int num, void* arg) { + int i; + + for (i = 0; i < num; i++) { + pipe_conns[i].i = i; + } +} + + +static void tcp_make_connect(conn_rec* p) { + struct sockaddr_in addr; + int r; + + r = uv_tcp_init(loop, (uv_tcp_t*)&p->stream); + ASSERT(r == 0); + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_tcp_connect(&((tcp_conn_rec*)p)->conn_req, (uv_tcp_t*)&p->stream, addr, connect_cb); + if (r) { + fprintf(stderr, "uv_tcp_connect error %s\n", + uv_err_name(uv_last_error(loop))); + ASSERT(0); + } + +#if DEBUG + printf("make connect %d\n", p->i); +#endif + + p->conn_req.data = p; + p->write_req.data = p; + p->stream.data = p; +} + + +static void pipe_make_connect(conn_rec* p) { + int r; + + r = uv_pipe_init(loop, (uv_pipe_t*)&p->stream); + ASSERT(r == 0); + + r = uv_pipe_connect(&((pipe_conn_rec*)p)->conn_req, (uv_pipe_t*)&p->stream, TEST_PIPENAME, connect_cb); + if (r) { + fprintf(stderr, "uv_tcp_connect error %s\n", + uv_err_name(uv_last_error(loop))); + ASSERT(0); + } + +#if DEBUG + printf("make connect %d\n", p->i); +#endif + + p->conn_req.data = p; + p->write_req.data = p; + p->stream.data = p; +} + + +static int tcp_do_connect(int num, make_connect_fn make_connect, void* arg) { + int i; + + for (i = 0; i < num; i++) { + tcp_make_connect((conn_rec*)&tcp_conns[i]); + tcp_conns[i].make_connect = make_connect; + } + + return 0; +} + + +static int pipe_do_connect(int num, make_connect_fn make_connect, void* arg) { + int i; + + for (i = 0; i < num; i++) { + pipe_make_connect((conn_rec*)&pipe_conns[i]); + pipe_conns[i].make_connect = make_connect; + } + + return 0; +} + + +static int pound_it(int concurrency, + const char* type, + setup_fn do_setup, + connect_fn do_connect, + make_connect_fn make_connect, + void* arg) { + double secs; + int r; + uint64_t start_time; /* in ns */ + uint64_t end_time; + + loop = uv_default_loop(); + + uv_update_time(loop); + start = uv_now(loop); + + /* Run benchmark for at least five seconds. */ + start_time = uv_hrtime(); + + do_setup(concurrency, arg); + + r = do_connect(concurrency, make_connect, arg); + ASSERT(!r); + + uv_run(loop); + + end_time = uv_hrtime(); + + /* Number of fractional seconds it took to run the benchmark. */ + secs = (double)(end_time - start_time) / NANOSEC; + + LOGF("%s-conn-pound-%d: %.0f accepts/s (%d failed)\n", + type, + concurrency, + closed_streams / secs, + conns_failed); + + return 0; +} + + +BENCHMARK_IMPL(tcp4_pound_100) { + return pound_it(100, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL); +} + + +BENCHMARK_IMPL(tcp4_pound_1000) { + return pound_it(1000, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL); +} + + +BENCHMARK_IMPL(pipe_pound_100) { + return pound_it(100, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL); +} + + +BENCHMARK_IMPL(pipe_pound_1000) { + return pound_it(1000, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL); +} diff --git a/src/rt/libuv/test/benchmark-pump.c b/src/rt/libuv/test/benchmark-pump.c index cd9c7d99679..d0b09301570 100644 --- a/src/rt/libuv/test/benchmark-pump.c +++ b/src/rt/libuv/test/benchmark-pump.c @@ -41,11 +41,14 @@ static void maybe_connect_some(); static uv_req_t* req_alloc(); static void req_free(uv_req_t* uv_req); -static uv_buf_t buf_alloc(uv_stream_t*, size_t size); +static uv_buf_t buf_alloc(uv_handle_t*, size_t size); static void buf_free(uv_buf_t uv_buf_t); +static uv_loop_t* loop; -static uv_tcp_t server; +static uv_tcp_t tcpServer; +static uv_pipe_t pipeServer; +static uv_stream_t* server; static struct sockaddr_in listen_addr; static struct sockaddr_in connect_addr; @@ -68,7 +71,10 @@ static char write_buffer[WRITE_BUFFER_SIZE]; /* Make this as large as you need. */ #define MAX_WRITE_HANDLES 1000 -static uv_tcp_t write_handles[MAX_WRITE_HANDLES]; +static stream_type type; + +static uv_tcp_t tcp_write_handles[MAX_WRITE_HANDLES]; +static uv_pipe_t pipe_write_handles[MAX_WRITE_HANDLES]; static uv_timer_t timer_handle; @@ -81,6 +87,7 @@ static double gbit(int64_t bytes, int64_t passed_ms) { static void show_stats(uv_timer_t* handle, int status) { int64_t diff; + int i; #if PRINT_STATS LOGF("connections: %d, write: %.1f gbit/s\n", @@ -91,12 +98,16 @@ static void show_stats(uv_timer_t* handle, int status) { /* Exit if the show is over */ if (!--stats_left) { - uv_update_time(); - diff = uv_now() - start_time; + uv_update_time(loop); + diff = uv_now(loop) - start_time; - LOGF("pump%d_client: %.1f gbit/s\n", write_sockets, + LOGF("%s_pump%d_client: %.1f gbit/s\n", type == TCP ? "tcp" : "pipe", write_sockets, gbit(nsent_total, diff)); + for (i = 0; i < write_sockets; i++) { + uv_close(type == TCP ? (uv_handle_t*)&tcp_write_handles[i] : (uv_handle_t*)&pipe_write_handles[i], NULL); + } + exit(0); } @@ -109,10 +120,10 @@ static void show_stats(uv_timer_t* handle, int status) { static void read_show_stats() { int64_t diff; - uv_update_time(); - diff = uv_now() - start_time; + uv_update_time(loop); + diff = uv_now(loop) - start_time; - LOGF("pump%d_server: %.1f gbit/s\n", max_read_sockets, + LOGF("%s_pump%d_server: %.1f gbit/s\n", type == TCP ? "tcp" : "pipe", max_read_sockets, gbit(nrecv_total, diff)); } @@ -131,38 +142,37 @@ void read_sockets_close_cb(uv_handle_t* handle) { /* If it's past the first second and everyone has closed their connection * Then print stats. */ - if (uv_now() - start_time > 1000 && read_sockets == 0) { + if (uv_now(loop) - start_time > 1000 && read_sockets == 0) { read_show_stats(); - uv_close((uv_handle_t*)&server, NULL); + uv_close((uv_handle_t*)server, NULL); } } static void start_stats_collection() { - uv_req_t* req = req_alloc(); int r; /* Show-stats timer */ stats_left = STATS_COUNT; - r = uv_timer_init(&timer_handle); + r = uv_timer_init(loop, &timer_handle); ASSERT(r == 0); r = uv_timer_start(&timer_handle, show_stats, STATS_INTERVAL, STATS_INTERVAL); ASSERT(r == 0); - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); } -static void read_cb(uv_stream_t* tcp, ssize_t bytes, uv_buf_t buf) { +static void read_cb(uv_stream_t* stream, ssize_t bytes, uv_buf_t buf) { if (nrecv_total == 0) { ASSERT(start_time == 0); - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); } if (bytes < 0) { - uv_close((uv_handle_t*)tcp, read_sockets_close_cb); + uv_close((uv_handle_t*)stream, read_sockets_close_cb); return; } @@ -173,46 +183,42 @@ static void read_cb(uv_stream_t* tcp, ssize_t bytes, uv_buf_t buf) { } -static void write_cb(uv_req_t *req, int status) { - uv_buf_t* buf = (uv_buf_t*) req->data; - +static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); - req_free(req); + req_free((uv_req_t*) req); nsent += sizeof write_buffer; nsent_total += sizeof write_buffer; - do_write((uv_stream_t*)req->handle); + do_write((uv_stream_t*) req->handle); } -static void do_write(uv_stream_t* tcp) { - uv_req_t* req; +static void do_write(uv_stream_t* stream) { + uv_write_t* req; uv_buf_t buf; int r; buf.base = (char*) &write_buffer; buf.len = sizeof write_buffer; - while (tcp->write_queue_size == 0) { - req = req_alloc(); - uv_req_init(req, (uv_handle_t*)tcp, write_cb); - - r = uv_write(req, &buf, 1); + while (stream->write_queue_size == 0) { + req = (uv_write_t*) req_alloc(); + r = uv_write(req, stream, &buf, 1, write_cb); ASSERT(r == 0); } } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { int i; - if (status) LOG(uv_strerror(uv_last_error())); + if (status) LOG(uv_strerror(uv_last_error(loop))); ASSERT(status == 0); write_sockets++; - req_free(req); + req_free((uv_req_t*) req); maybe_connect_some(); @@ -221,47 +227,64 @@ static void connect_cb(uv_req_t* req, int status) { /* Yay! start writing */ for (i = 0; i < write_sockets; i++) { - do_write((uv_stream_t*)&write_handles[i]); + do_write(type == TCP ? (uv_stream_t*)&tcp_write_handles[i] : (uv_stream_t*)&pipe_write_handles[i]); } } } static void maybe_connect_some() { - uv_req_t* req; + uv_connect_t* req; uv_tcp_t* tcp; + uv_pipe_t* pipe; int r; while (max_connect_socket < TARGET_CONNECTIONS && max_connect_socket < write_sockets + MAX_SIMULTANEOUS_CONNECTS) { - tcp = &write_handles[max_connect_socket++]; + if (type == TCP) { + tcp = &tcp_write_handles[max_connect_socket++]; - r = uv_tcp_init(tcp); - ASSERT(r == 0); + r = uv_tcp_init(loop, tcp); + ASSERT(r == 0); - req = req_alloc(); - uv_req_init(req, (uv_handle_t*)tcp, connect_cb); - r = uv_tcp_connect(req, connect_addr); - ASSERT(r == 0); + req = (uv_connect_t*) req_alloc(); + r = uv_tcp_connect(req, tcp, connect_addr, connect_cb); + ASSERT(r == 0); + } else { + pipe = &pipe_write_handles[max_connect_socket++]; + + r = uv_pipe_init(loop, pipe); + ASSERT(r == 0); + + req = (uv_connect_t*) req_alloc(); + r = uv_pipe_connect(req, pipe, TEST_PIPENAME, connect_cb); + ASSERT(r == 0); + } } } -static void connection_cb(uv_handle_t* s, int status) { - uv_tcp_t* tcp; +static void connection_cb(uv_stream_t* s, int status) { + uv_stream_t* stream; int r; - ASSERT(&server == (uv_tcp_t*)s); + ASSERT(server == s); ASSERT(status == 0); - tcp = malloc(sizeof(uv_tcp_t)); + if (type == TCP) { + stream = (uv_stream_t*)malloc(sizeof(uv_tcp_t)); + r = uv_tcp_init(loop, (uv_tcp_t*)stream); + ASSERT(r == 0); + } else { + stream = (uv_stream_t*)malloc(sizeof(uv_pipe_t)); + r = uv_pipe_init(loop, (uv_pipe_t*)stream); + ASSERT(r == 0); + } - uv_tcp_init(tcp); - - r = uv_accept(s, (uv_stream_t*)tcp); + r = uv_accept(s, stream); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)tcp, buf_alloc, read_cb); + r = uv_read_start(stream, buf_alloc, read_cb); ASSERT(r == 0); read_sockets++; @@ -274,7 +297,7 @@ static void connection_cb(uv_handle_t* s, int status) { */ typedef struct req_list_s { - uv_req_t uv_req; + union uv_any_req uv_req; struct req_list_s* next; } req_list_t; @@ -317,7 +340,7 @@ typedef struct buf_list_s { static buf_list_t* buf_freelist = NULL; -static uv_buf_t buf_alloc(uv_stream_t* tcp, size_t size) { +static uv_buf_t buf_alloc(uv_handle_t* handle, size_t size) { buf_list_t* buf; buf = buf_freelist; @@ -342,48 +365,99 @@ static void buf_free(uv_buf_t uv_buf_t) { } -HELPER_IMPL(pump_server) { +HELPER_IMPL(tcp_pump_server) { int r; - uv_init(); + type = TCP; + loop = uv_default_loop(); + listen_addr = uv_ip4_addr("0.0.0.0", TEST_PORT); /* Server */ - r = uv_tcp_init(&server); + server = (uv_stream_t*)&tcpServer; + r = uv_tcp_init(loop, &tcpServer); ASSERT(r == 0); - r = uv_tcp_bind(&server, listen_addr); + r = uv_tcp_bind(&tcpServer, listen_addr); ASSERT(r == 0); - r = uv_tcp_listen(&server, MAX_WRITE_HANDLES, connection_cb); + r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); - uv_run(); + uv_run(loop); return 0; } -void pump(int n) { +HELPER_IMPL(pipe_pump_server) { + int r; + type = PIPE; + + loop = uv_default_loop(); + + /* Server */ + server = (uv_stream_t*)&pipeServer; + r = uv_pipe_init(loop, &pipeServer); + ASSERT(r == 0); + r = uv_pipe_bind(&pipeServer, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); + ASSERT(r == 0); + + uv_run(loop); + + return 0; +} + + +void tcp_pump(int n) { ASSERT(n <= MAX_WRITE_HANDLES); TARGET_CONNECTIONS = n; + type = TCP; - uv_init(); + loop = uv_default_loop(); connect_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); /* Start making connections */ maybe_connect_some(); - uv_run(); + uv_run(loop); } -BENCHMARK_IMPL(pump100_client) { - pump(100); +void pipe_pump(int n) { + ASSERT(n <= MAX_WRITE_HANDLES); + TARGET_CONNECTIONS = n; + type = PIPE; + + loop = uv_default_loop(); + + /* Start making connections */ + maybe_connect_some(); + + uv_run(loop); +} + + +BENCHMARK_IMPL(tcp_pump100_client) { + tcp_pump(100); return 0; } -BENCHMARK_IMPL(pump1_client) { - pump(1); +BENCHMARK_IMPL(tcp_pump1_client) { + tcp_pump(1); + return 0; +} + + +BENCHMARK_IMPL(pipe_pump100_client) { + pipe_pump(100); + return 0; +} + + +BENCHMARK_IMPL(pipe_pump1_client) { + pipe_pump(1); return 0; } diff --git a/src/rt/libuv/test/benchmark-sizes.c b/src/rt/libuv/test/benchmark-sizes.c index 830de3a007c..3967f301f9f 100644 --- a/src/rt/libuv/test/benchmark-sizes.c +++ b/src/rt/libuv/test/benchmark-sizes.c @@ -24,12 +24,16 @@ BENCHMARK_IMPL(sizes) { - LOGF("uv_req_t: %u bytes\n", (unsigned int) sizeof(uv_req_t)); + LOGF("uv_shutdown_t: %u bytes\n", (unsigned int) sizeof(uv_shutdown_t)); + LOGF("uv_write_t: %u bytes\n", (unsigned int) sizeof(uv_write_t)); + LOGF("uv_connect_t: %u bytes\n", (unsigned int) sizeof(uv_connect_t)); LOGF("uv_tcp_t: %u bytes\n", (unsigned int) sizeof(uv_tcp_t)); + LOGF("uv_pipe_t: %u bytes\n", (unsigned int) sizeof(uv_pipe_t)); LOGF("uv_prepare_t: %u bytes\n", (unsigned int) sizeof(uv_prepare_t)); LOGF("uv_check_t: %u bytes\n", (unsigned int) sizeof(uv_check_t)); LOGF("uv_idle_t: %u bytes\n", (unsigned int) sizeof(uv_idle_t)); LOGF("uv_async_t: %u bytes\n", (unsigned int) sizeof(uv_async_t)); LOGF("uv_timer_t: %u bytes\n", (unsigned int) sizeof(uv_timer_t)); + LOGF("uv_process_t: %u bytes\n", (unsigned int) sizeof(uv_process_t)); return 0; } diff --git a/src/rt/libuv/test/benchmark-spawn.c b/src/rt/libuv/test/benchmark-spawn.c new file mode 100644 index 00000000000..6e5493d529b --- /dev/null +++ b/src/rt/libuv/test/benchmark-spawn.c @@ -0,0 +1,156 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* This benchmark spawns itself 1000 times. */ + +#include "task.h" +#include "uv.h" + +static uv_loop_t* loop; + +static int N = 1000; +static int done; + +static uv_process_t process; +static uv_process_options_t options = { 0 }; +static char exepath[1024]; +static size_t exepath_size = 1024; +static char* args[3]; +static uv_pipe_t out; + +#define OUTPUT_SIZE 1024 +static char output[OUTPUT_SIZE]; +static int output_used; + +static int process_open; +static int pipe_open; + + +static void spawn(); + + +void maybe_spawn() { + if (process_open == 0 && pipe_open == 0) { + done++; + if (done < N) { + spawn(); + } + } +} + + +static void process_close_cb(uv_handle_t* handle) { + ASSERT(process_open == 1); + process_open = 0; + maybe_spawn(); +} + + +static void exit_cb(uv_process_t* process, int exit_status, int term_signal) { + ASSERT(exit_status == 42); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, process_close_cb); +} + + +uv_buf_t on_alloc(uv_handle_t* handle, size_t suggested_size) { + uv_buf_t buf; + buf.base = output + output_used; + buf.len = OUTPUT_SIZE - output_used; + return buf; +} + + +void pipe_close_cb(uv_handle_t* pipe) { + ASSERT(pipe_open == 1); + pipe_open = 0; + maybe_spawn(); +} + + +void on_read(uv_stream_t* pipe, ssize_t nread, uv_buf_t buf) { + uv_err_t err = uv_last_error(loop); + + if (nread > 0) { + ASSERT(pipe_open == 1); + output_used += nread; + } else if (nread < 0) { + if (err.code == UV_EOF) { + uv_close((uv_handle_t*)pipe, pipe_close_cb); + } + } +} + + +static void spawn() { + int r; + + ASSERT(process_open == 0); + ASSERT(pipe_open == 0); + + args[0] = exepath; + args[1] = "spawn_helper"; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; + + uv_pipe_init(loop, &out); + options.stdout_stream = &out; + + r = uv_spawn(loop, &process, options); + ASSERT(r == 0); + + process_open = 1; + pipe_open = 1; + output_used = 0; + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); +} + + +BENCHMARK_IMPL(spawn) { + int r; + static int64_t start_time, end_time; + + loop = uv_default_loop(); + + r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + + uv_update_time(loop); + start_time = uv_now(loop); + + spawn(); + + r = uv_run(loop); + ASSERT(r == 0); + + uv_update_time(loop); + end_time = uv_now(loop); + + LOGF("spawn: %.0f spawns/s\n", + (double) N / (double) (end_time - start_time) * 1000.0); + + return 0; +} diff --git a/src/rt/libuv/test/benchmark-udp-packet-storm.c b/src/rt/libuv/test/benchmark-udp-packet-storm.c new file mode 100644 index 00000000000..24a9e1b920d --- /dev/null +++ b/src/rt/libuv/test/benchmark-udp-packet-storm.c @@ -0,0 +1,249 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "task.h" +#include "uv.h" + +#include +#include +#include + +#define EXPECTED "RANG TANG DING DONG I AM THE JAPANESE SANDMAN" /* "Take eight!" */ + +#define TEST_DURATION 5000 /* ms */ + +#define MAX_SENDERS 1000 +#define MAX_RECEIVERS 1000 + +#define BASE_PORT 12345 + +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) + +static uv_loop_t* loop; + +static int n_senders_; +static int n_receivers_; +static uv_udp_t senders[MAX_SENDERS]; +static uv_udp_t receivers[MAX_RECEIVERS]; +static uv_buf_t bufs[5]; + +static int send_cb_called; +static int recv_cb_called; +static int close_cb_called; +static int stopping = 0; + +typedef struct { + struct sockaddr_in addr; +} sender_state_t; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + ASSERT(suggested_size <= sizeof slab); + return uv_buf_init(slab, sizeof slab); +} + + +static void send_cb(uv_udp_send_t* req, int status) { + sender_state_t* ss; + int r; + + if (stopping) { + return; + } + + ASSERT(req != NULL); + ASSERT(status == 0); + + ss = req->data; + + r = uv_udp_send(req, req->handle, bufs, ARRAY_SIZE(bufs), ss->addr, send_cb); + ASSERT(r == 0); + + req->data = ss; + + send_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + if (nread == 0) + return; + + if (nread == -1) { + ASSERT(uv_last_error(loop).code == UV_EINTR); /* FIXME change error code */ + return; + } + + ASSERT(addr->sa_family == AF_INET); + ASSERT(!memcmp(buf.base, EXPECTED, nread)); + + recv_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void timeout_cb(uv_timer_t* timer, int status) { + int i; + + stopping = 1; + + for (i = 0; i < n_senders_; i++) + uv_close((uv_handle_t*)&senders[i], close_cb); + + for (i = 0; i < n_receivers_; i++) + uv_close((uv_handle_t*)&receivers[i], close_cb); +} + + +static int do_packet_storm(int n_senders, int n_receivers) { + uv_timer_t timeout; + sender_state_t *ss; + uv_udp_send_t* req; + uv_udp_t* handle; + int i; + int r; + + ASSERT(n_senders <= MAX_SENDERS); + ASSERT(n_receivers <= MAX_RECEIVERS); + + loop = uv_default_loop(); + + n_senders_ = n_senders; + n_receivers_ = n_receivers; + + r = uv_timer_init(loop, &timeout); + ASSERT(r == 0); + + r = uv_timer_start(&timeout, timeout_cb, TEST_DURATION, 0); + ASSERT(r == 0); + + /* Timer should not keep loop alive. */ + uv_unref(loop); + + for (i = 0; i < n_receivers; i++) { + struct sockaddr_in addr; + handle = &receivers[i]; + + r = uv_udp_init(loop, handle); + ASSERT(r == 0); + + addr = uv_ip4_addr("0.0.0.0", BASE_PORT + i); + + r = uv_udp_bind(handle, addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(handle, alloc_cb, recv_cb); + ASSERT(r == 0); + } + + bufs[0] = uv_buf_init(EXPECTED + 0, 10); + bufs[1] = uv_buf_init(EXPECTED + 10, 10); + bufs[2] = uv_buf_init(EXPECTED + 20, 10); + bufs[3] = uv_buf_init(EXPECTED + 30, 10); + bufs[4] = uv_buf_init(EXPECTED + 40, 5); + + for (i = 0; i < n_senders; i++) { + handle = &senders[i]; + + r = uv_udp_init(loop, handle); + ASSERT(r == 0); + + req = malloc(sizeof(*req) + sizeof(*ss)); + + ss = (void*)(req + 1); + ss->addr = uv_ip4_addr("127.0.0.1", BASE_PORT + (i % n_receivers)); + + r = uv_udp_send(req, handle, bufs, ARRAY_SIZE(bufs), ss->addr, send_cb); + ASSERT(r == 0); + + req->data = ss; + } + + uv_run(loop); + + printf("udp_packet_storm_%dv%d: %.0f/s received, %.0f/s sent\n", + n_receivers, + n_senders, + recv_cb_called / (TEST_DURATION / 1000.0), + send_cb_called / (TEST_DURATION / 1000.0)); + + return 0; +} + + +BENCHMARK_IMPL(udp_packet_storm_1v1) { + return do_packet_storm(1, 1); +} + + +BENCHMARK_IMPL(udp_packet_storm_1v10) { + return do_packet_storm(1, 10); +} + + +BENCHMARK_IMPL(udp_packet_storm_1v100) { + return do_packet_storm(1, 100); +} + + +BENCHMARK_IMPL(udp_packet_storm_1v1000) { + return do_packet_storm(1, 1000); +} + + +BENCHMARK_IMPL(udp_packet_storm_10v10) { + return do_packet_storm(10, 10); +} + + +BENCHMARK_IMPL(udp_packet_storm_10v100) { + return do_packet_storm(10, 100); +} + + +BENCHMARK_IMPL(udp_packet_storm_10v1000) { + return do_packet_storm(10, 1000); +} + + +BENCHMARK_IMPL(udp_packet_storm_100v100) { + return do_packet_storm(100, 100); +} + + +BENCHMARK_IMPL(udp_packet_storm_100v1000) { + return do_packet_storm(100, 1000); +} + + +BENCHMARK_IMPL(udp_packet_storm_1000v1000) { + return do_packet_storm(1000, 1000); +} diff --git a/src/rt/libuv/test/dns-server.c b/src/rt/libuv/test/dns-server.c index 361c9d080ba..086b52d1a36 100644 --- a/src/rt/libuv/test/dns-server.c +++ b/src/rt/libuv/test/dns-server.c @@ -27,7 +27,7 @@ typedef struct { - uv_req_t req; + uv_write_t req; uv_buf_t buf; } write_req_t; @@ -47,15 +47,18 @@ typedef struct { } dnshandle; +static uv_loop_t* loop; + + static int server_closed; static uv_tcp_t server; -static void after_write(uv_req_t* req, int status); +static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, uv_buf_t buf); static void on_close(uv_handle_t* peer); static void on_server_close(uv_handle_t* handle); -static void on_connection(uv_handle_t*, int status); +static void on_connection(uv_stream_t*, int status); #define WRITE_BUF_LEN (64*1024) #define DNSREC_LEN (4) @@ -67,11 +70,11 @@ unsigned char qrecord[] = {5, 'e', 'c', 'h', 'o', 's', 3, 's', 'r', 'v', 0, 0, 1 unsigned char arecord[] = {0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 5, 0xbd, 0, 4, 10, 0, 1, 1 }; -static void after_write(uv_req_t* req, int status) { +static void after_write(uv_write_t* req, int status) { write_req_t* wr; if (status) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(loop); fprintf(stderr, "uv_write error: %s\n", uv_strerror(err)); ASSERT(0); } @@ -84,8 +87,8 @@ static void after_write(uv_req_t* req, int status) { } -static void after_shutdown(uv_req_t* req, int status) { - uv_close(req->handle, on_close); +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*) req->handle, on_close); free(req); } @@ -116,7 +119,7 @@ static void addrsp(write_req_t* wr, char* hdr) { } static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { - write_req_t *wr; + write_req_t* wr; dnshandle* dns = (dnshandle*)handle; char hdrbuf[DNSREC_LEN]; int hdrbuf_remaining = DNSREC_LEN; @@ -127,7 +130,6 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { int usingprev = 0; wr = (write_req_t*) malloc(sizeof *wr); - uv_req_init(&wr->req, (uv_handle_t*)handle, after_write); wr->buf.base = (char*)malloc(WRITE_BUF_LEN); wr->buf.len = 0; @@ -165,7 +167,7 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { rec_remaining = ntohs(reclen_n) - (DNSREC_LEN - 2); } } - + if (rec_remaining <= readbuf_remaining) { /* prepare reply */ addrsp(wr, hdrbuf); @@ -197,7 +199,7 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { /* send write buffer */ if (wr->buf.len > 0) { - if (uv_write(&wr->req, &wr->buf, 1)) { + if (uv_write((uv_write_t*) &wr->req, handle, &wr->buf, 1, after_write)) { FATAL("uv_write failed"); } } @@ -217,19 +219,18 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { } static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { - uv_req_t* req; + uv_shutdown_t* req; if (nread < 0) { /* Error or EOF */ - ASSERT (uv_last_error().code == UV_EOF); + ASSERT (uv_last_error(loop).code == UV_EOF); if (buf.base) { free(buf.base); } - req = (uv_req_t*) malloc(sizeof *req); - uv_req_init(req, (uv_handle_t*)handle, after_shutdown); - uv_shutdown(req); + req = malloc(sizeof *req); + uv_shutdown(req, handle, after_shutdown); return; } @@ -249,7 +250,7 @@ static void on_close(uv_handle_t* peer) { } -static uv_buf_t buf_alloc(uv_stream_t* handle, size_t suggested_size) { +static uv_buf_t buf_alloc(uv_handle_t* handle, size_t suggested_size) { uv_buf_t buf; buf.base = (char*) malloc(suggested_size); buf.len = suggested_size; @@ -257,7 +258,7 @@ static uv_buf_t buf_alloc(uv_stream_t* handle, size_t suggested_size) { } -static void on_connection(uv_handle_t* server, int status) { +static void on_connection(uv_stream_t* server, int status) { dnshandle* handle; int r; @@ -271,7 +272,8 @@ static void on_connection(uv_handle_t* server, int status) { handle->state.prevbuf_pos = 0; handle->state.prevbuf_rem = 0; - uv_tcp_init((uv_tcp_t*)handle); + r = uv_tcp_init(loop, (uv_tcp_t*)handle); + ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)handle); ASSERT(r == 0); @@ -290,7 +292,7 @@ static int dns_start(int port) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", port); int r; - r = uv_tcp_init(&server); + r = uv_tcp_init(loop, &server); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); @@ -304,7 +306,7 @@ static int dns_start(int port) { return 1; } - r = uv_tcp_listen(&server, 128, on_connection); + r = uv_listen((uv_stream_t*)&server, 128, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error\n"); @@ -316,10 +318,11 @@ static int dns_start(int port) { HELPER_IMPL(dns_server) { - uv_init(); + loop = uv_default_loop(); + if (dns_start(TEST_PORT_2)) return 1; - uv_run(); + uv_run(loop); return 0; } diff --git a/src/rt/libuv/test/echo-server.c b/src/rt/libuv/test/echo-server.c index 9addc546c1e..453ada66d24 100644 --- a/src/rt/libuv/test/echo-server.c +++ b/src/rt/libuv/test/echo-server.c @@ -24,32 +24,31 @@ #include #include - typedef struct { - uv_req_t req; + uv_write_t req; uv_buf_t buf; } write_req_t; +static uv_loop_t* loop; static int server_closed; -static uv_tcp_t server; +static stream_type serverType; +static uv_tcp_t tcpServer; +static uv_pipe_t pipeServer; +static uv_handle_t* server; -static int server6_closed; -static uv_tcp_t server6; - - -static void after_write(uv_req_t* req, int status); +static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, uv_buf_t buf); static void on_close(uv_handle_t* peer); static void on_server_close(uv_handle_t* handle); -static void on_connection(uv_handle_t*, int status); +static void on_connection(uv_stream_t*, int status); -static void after_write(uv_req_t* req, int status) { +static void after_write(uv_write_t* req, int status) { write_req_t* wr; if (status) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(loop); fprintf(stderr, "uv_write error: %s\n", uv_strerror(err)); ASSERT(0); } @@ -62,8 +61,8 @@ static void after_write(uv_req_t* req, int status) { } -static void after_shutdown(uv_req_t* req, int status) { - uv_close(req->handle, on_close); +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*)req->handle, on_close); free(req); } @@ -71,19 +70,18 @@ static void after_shutdown(uv_req_t* req, int status) { static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { int i; write_req_t *wr; - uv_req_t* req; + uv_shutdown_t* req; if (nread < 0) { /* Error or EOF */ - ASSERT (uv_last_error().code == UV_EOF); + ASSERT (uv_last_error(loop).code == UV_EOF); if (buf.base) { free(buf.base); } - req = (uv_req_t*) malloc(sizeof *req); - uv_req_init(req, (uv_handle_t*)handle, (void *(*)(void *))after_shutdown); - uv_shutdown(req); + req = (uv_shutdown_t*) malloc(sizeof *req); + uv_shutdown(req, handle, after_shutdown); return; } @@ -94,24 +92,29 @@ static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { return; } - /* Scan for the letter Q which signals that we should quit. */ + /* + * Scan for the letter Q which signals that we should quit the server. + * If we get QS it means close the stream. + */ if (!server_closed) { for (i = 0; i < nread; i++) { if (buf.base[i] == 'Q') { - uv_close((uv_handle_t*)&server, on_server_close); - server_closed = 1; - uv_close((uv_handle_t*)&server6, on_server_close); - server6_closed = 1; + if (i + 1 < nread && buf.base[i + 1] == 'S') { + free(buf.base); + uv_close((uv_handle_t*)handle, on_close); + return; + } else { + uv_close(server, on_server_close); + server_closed = 1; + } } } } wr = (write_req_t*) malloc(sizeof *wr); - uv_req_init(&wr->req, (uv_handle_t*)handle, (void *(*)(void *))after_write); - wr->buf.base = buf.base; - wr->buf.len = nread; - if (uv_write(&wr->req, &wr->buf, 1)) { + wr->buf = uv_buf_init(buf.base, nread); + if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) { FATAL("uv_write failed"); } } @@ -122,71 +125,98 @@ static void on_close(uv_handle_t* peer) { } -static uv_buf_t echo_alloc(uv_stream_t* handle, size_t suggested_size) { - uv_buf_t buf; - buf.base = (char*) malloc(suggested_size); - buf.len = suggested_size; - return buf; +static uv_buf_t echo_alloc(uv_handle_t* handle, size_t suggested_size) { + return uv_buf_init(malloc(suggested_size), suggested_size); } -static void on_connection(uv_handle_t* server, int status) { - uv_tcp_t* handle; +static void on_connection(uv_stream_t* server, int status) { + uv_stream_t* stream; int r; if (status != 0) { - fprintf(stderr, "Connect error %d\n", uv_last_error().code); + fprintf(stderr, "Connect error %d\n", + uv_last_error(loop).code); } ASSERT(status == 0); - handle = (uv_tcp_t*) malloc(sizeof *handle); - ASSERT(handle != NULL); + switch (serverType) { + case TCP: + stream = malloc(sizeof(uv_tcp_t)); + ASSERT(stream != NULL); + r = uv_tcp_init(loop, (uv_tcp_t*)stream); + ASSERT(r == 0); + break; - uv_tcp_init(handle); + case PIPE: + stream = malloc(sizeof(uv_pipe_t)); + ASSERT(stream != NULL); + r = uv_pipe_init(loop, (uv_pipe_t*)stream); + ASSERT(r == 0); + break; + + default: + ASSERT(0 && "Bad serverType"); + abort(); + } /* associate server with stream */ - handle->data = server; + stream->data = server; - r = uv_accept(server, (uv_stream_t*)handle); + r = uv_accept(server, stream); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)handle, echo_alloc, after_read); + r = uv_read_start(stream, echo_alloc, after_read); ASSERT(r == 0); } static void on_server_close(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*)&server || handle == (uv_handle_t*)&server6); + ASSERT(handle == server); } -static int echo_start(int port) { +static int tcp4_echo_start(int port) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", port); - struct sockaddr_in6 addr6 = uv_ip6_addr("::1", port); int r; - r = uv_tcp_init(&server); + server = (uv_handle_t*)&tcpServer; + serverType = TCP; + + r = uv_tcp_init(loop, &tcpServer); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); return 1; } - r = uv_tcp_bind(&server, addr); + r = uv_tcp_bind(&tcpServer, addr); if (r) { /* TODO: Error codes */ fprintf(stderr, "Bind error\n"); return 1; } - r = uv_tcp_listen(&server, 128, on_connection); + r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); if (r) { /* TODO: Error codes */ - fprintf(stderr, "Listen error\n"); + fprintf(stderr, "Listen error %s\n", + uv_err_name(uv_last_error(loop))); return 1; } - r = uv_tcp_init(&server6); + return 0; +} + + +static int tcp6_echo_start(int port) { + struct sockaddr_in6 addr6 = uv_ip6_addr("::1", port); + int r; + + server = (uv_handle_t*)&tcpServer; + serverType = TCP; + + r = uv_tcp_init(loop, &tcpServer); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); @@ -194,17 +224,17 @@ static int echo_start(int port) { } /* IPv6 is optional as not all platforms support it */ - r = uv_tcp_bind6(&server6, addr6); + r = uv_tcp_bind6(&tcpServer, addr6); if (r) { /* show message but return OK */ fprintf(stderr, "IPv6 not supported\n"); return 0; } - r = uv_tcp_listen(&server6, 128, on_connection); + r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); if (r) { /* TODO: Error codes */ - fprintf(stderr, "Listen error on IPv6\n"); + fprintf(stderr, "Listen error\n"); return 1; } @@ -212,11 +242,65 @@ static int echo_start(int port) { } -HELPER_IMPL(echo_server) { - uv_init(); - if (echo_start(TEST_PORT)) - return 1; +static int pipe_echo_start(char* pipeName) { + int r; + + server = (uv_handle_t*)&pipeServer; + serverType = PIPE; + + r = uv_pipe_init(loop, &pipeServer); + if (r) { + fprintf(stderr, "uv_pipe_init: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } + + r = uv_pipe_bind(&pipeServer, pipeName); + if (r) { + fprintf(stderr, "uv_pipe_bind: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } + + r = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, on_connection); + if (r) { + fprintf(stderr, "uv_pipe_listen: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } - uv_run(); + return 0; +} + + +HELPER_IMPL(tcp4_echo_server) { + loop = uv_default_loop(); + + if (tcp4_echo_start(TEST_PORT)) + return 1; + + uv_run(loop); + return 0; +} + + +HELPER_IMPL(tcp6_echo_server) { + loop = uv_default_loop(); + + if (tcp6_echo_start(TEST_PORT)) + return 1; + + uv_run(loop); + return 0; +} + + +HELPER_IMPL(pipe_echo_server) { + loop = uv_default_loop(); + + if (pipe_echo_start(TEST_PIPENAME)) + return 1; + + uv_run(loop); return 0; } diff --git a/src/rt/libuv/test/run-benchmarks.c b/src/rt/libuv/test/run-benchmarks.c index 43d25a63bbd..af11beb9b4c 100644 --- a/src/rt/libuv/test/run-benchmarks.c +++ b/src/rt/libuv/test/run-benchmarks.c @@ -32,28 +32,33 @@ /* The time in milliseconds after which a single benchmark times out. */ #define BENCHMARK_TIMEOUT 60000 +static int maybe_run_test(int argc, char **argv); + int main(int argc, char **argv) { - task_entry_t *task; - platform_init(argc, argv); - if (argc > 1) { - /* A specific process was requested. */ - return run_process(argv[1]); - - } else { - /* Run all benchmarks. */ - task = (task_entry_t*)&TASKS; - for (task = (task_entry_t*)&TASKS; task->main; task++) { - if (task->is_helper) { - continue; - } - - run_task(task, BENCHMARK_TIMEOUT, 1); - } - LOG("Done.\n"); - - return 0; + switch (argc) { + case 1: return run_tests(BENCHMARK_TIMEOUT, 1); + case 2: return maybe_run_test(argc, argv); + case 3: return run_test_part(argv[1], argv[2]); + default: + LOGF("Too many arguments.\n"); + return 1; } } + + +static int maybe_run_test(int argc, char **argv) { + if (strcmp(argv[1], "--list") == 0) { + print_tests(stdout); + return 0; + } + + if (strcmp(argv[1], "spawn_helper") == 0) { + printf("hello world\n"); + return 42; + } + + return run_test(argv[1], BENCHMARK_TIMEOUT, 1); +} diff --git a/src/rt/libuv/test/run-tests.c b/src/rt/libuv/test/run-tests.c index f83006bdd81..a187869110a 100644 --- a/src/rt/libuv/test/run-tests.c +++ b/src/rt/libuv/test/run-tests.c @@ -28,59 +28,53 @@ /* Actual tests and helpers are defined in test-list.h */ #include "test-list.h" - /* The time in milliseconds after which a single test times out. */ #define TEST_TIMEOUT 5000 - -static void log_progress(int total, int passed, int failed, char* name) { - LOGF("[%% %3d|+ %3d|- %3d]: %s", (passed + failed) / total * 100, - passed, failed, name); -} +static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { - int total, passed, failed; - task_entry_t* task; - platform_init(argc, argv); - if (argc > 1) { - /* A specific process was requested. */ - return run_process(argv[1]); - - } else { - /* Count the number of tests. */ - total = 0; - task = (task_entry_t*)&TASKS; - for (task = (task_entry_t*)&TASKS; task->main; task++) { - if (!task->is_helper) { - total++; - } - } - - /* Run all tests. */ - passed = 0; - failed = 0; - task = (task_entry_t*)&TASKS; - for (task = (task_entry_t*)&TASKS; task->main; task++) { - if (task->is_helper) { - continue; - } - - rewind_cursor(); - log_progress(total, passed, failed, task->task_name); - - if (run_task(task, TEST_TIMEOUT, 0)) { - passed++; - } else { - failed++; - } - } - - rewind_cursor(); - log_progress(total, passed, failed, "Done.\n"); - - return 0; + switch (argc) { + case 1: return run_tests(TEST_TIMEOUT, 0); + case 2: return maybe_run_test(argc, argv); + case 3: return run_test_part(argv[1], argv[2]); + default: + LOGF("Too many arguments.\n"); + return 1; } } + + +static int maybe_run_test(int argc, char **argv) { + if (strcmp(argv[1], "--list") == 0) { + print_tests(stdout); + return 0; + } + + if (strcmp(argv[1], "spawn_helper1") == 0) { + return 1; + } + + if (strcmp(argv[1], "spawn_helper2") == 0) { + printf("hello world\n"); + return 1; + } + + if (strcmp(argv[1], "spawn_helper3") == 0) { + char buffer[256]; + fgets(buffer, sizeof(buffer) - 1, stdin); + buffer[sizeof(buffer) - 1] = '\0'; + fputs(buffer, stdout); + return 1; + } + + if (strcmp(argv[1], "spawn_helper4") == 0) { + /* Never surrender, never return! */ + while (1) uv_sleep(10000); + } + + return run_test(argv[1], TEST_TIMEOUT, 0); +} diff --git a/src/rt/libuv/test/runner-unix.c b/src/rt/libuv/test/runner-unix.c index fa4c63bb208..6033d64294d 100644 --- a/src/rt/libuv/test/runner-unix.c +++ b/src/rt/libuv/test/runner-unix.c @@ -65,12 +65,14 @@ void platform_init(int argc, char **argv) { #else strcpy(executable_path, argv[0]); #endif + + signal(SIGPIPE, SIG_IGN); } -/* Invoke "arv[0] test-name". Store process info in *p. */ +/* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ /* Make sure that all stdio output of the processes is buffered up. */ -int process_start(char* name, process_info_t* p) { +int process_start(char* name, char* part, process_info_t* p) { FILE* stdout_file = tmpfile(); if (!stdout_file) { perror("tmpfile"); @@ -92,7 +94,7 @@ int process_start(char* name, process_info_t* p) { dup2(fileno(stdout_file), STDOUT_FILENO); dup2(fileno(stdout_file), STDERR_FILENO); - char* args[3] = { executable_path, name, NULL }; + char* args[] = { executable_path, name, part, NULL }; execvp(executable_path, args); perror("execvp()"); _exit(127); diff --git a/src/rt/libuv/test/runner-win.c b/src/rt/libuv/test/runner-win.c index 09458a6b5ce..fc08839ee48 100644 --- a/src/rt/libuv/test/runner-win.c +++ b/src/rt/libuv/test/runner-win.c @@ -52,7 +52,7 @@ void platform_init(int argc, char **argv) { } -int process_start(char *name, process_info_t *p) { +int process_start(char *name, char *part, process_info_t *p) { HANDLE file = INVALID_HANDLE_VALUE; HANDLE nul = INVALID_HANDLE_VALUE; WCHAR path[MAX_PATH], filename[MAX_PATH]; @@ -97,12 +97,24 @@ int process_start(char *name, process_info_t *p) { if (result == 0 || result == sizeof(image)) goto error; - if (_snwprintf((wchar_t*)&args, - sizeof(args) / sizeof(wchar_t), - L"\"%s\" %S", - image, - name) < 0) - goto error; + if (part) { + if (_snwprintf((wchar_t*)args, + sizeof(args) / sizeof(wchar_t), + L"\"%s\" %S %S", + image, + name, + part) < 0) { + goto error; + } + } else { + if (_snwprintf((wchar_t*)args, + sizeof(args) / sizeof(wchar_t), + L"\"%s\" %S", + image, + name) < 0) { + goto error; + } + } memset((void*)&si, 0, sizeof(si)); si.cb = sizeof(si); diff --git a/src/rt/libuv/test/runner.c b/src/rt/libuv/test/runner.c index 7b6590f6792..73a3864ee41 100644 --- a/src/rt/libuv/test/runner.c +++ b/src/rt/libuv/test/runner.c @@ -26,102 +26,173 @@ char executable_path[PATHMAX] = { '\0' }; -/* Start a specific process declared by TEST_ENTRY or TEST_HELPER. */ -/* Returns the exit code of the specific process. */ -int run_process(char* name) { - task_entry_t *test; - for (test = (task_entry_t*)&TASKS; test->main; test++) { - if (strcmp(name, test->process_name) == 0) { - return test->main(); - } - } - - LOGF("Test process %s not found!\n", name); - return 255; +static void log_progress(int total, int passed, int failed, const char* name) { + LOGF("[%% %3d|+ %3d|- %3d]: %s", (passed + failed) / total * 100, + passed, failed, name); } -/* - * Runs all processes associated with a particular test or benchmark. - * It returns 1 if the test succeeded, 0 if it failed. - * If the test fails it prints diagnostic information. - * If benchmark_output is nonzero, the output from the main process is - * always shown. - */ -int run_task(task_entry_t *test, int timeout, int benchmark_output) { - int i, result, success; - char errmsg[256]; - task_entry_t *helper; - int process_count; - process_info_t processes[MAX_PROCESSES]; - process_info_t *main_process; +int run_tests(int timeout, int benchmark_output) { + int total, passed, failed; + task_entry_t* task; - success = 0; - - process_count = 0; - - /* Start all helpers for this test first. */ - for (helper = (task_entry_t*)&TASKS; helper->main; helper++) { - if (helper->is_helper && - strcmp(test->task_name, helper->task_name) == 0) { - if (process_start(helper->process_name, &processes[process_count]) == -1) { - snprintf((char*)&errmsg, - sizeof(errmsg), - "process `%s` failed to start.", - helper->process_name); - goto finalize; - } - process_count++; + /* Count the number of tests. */ + total = 0; + for (task = TASKS; task->main; task++) { + if (!task->is_helper) { + total++; } } - /* Wait a little bit to allow servers to start. Racy. */ - uv_sleep(100); + /* Run all tests. */ + passed = 0; + failed = 0; + for (task = TASKS; task->main; task++) { + if (task->is_helper) { + continue; + } - /* Start the main test process. */ - if (process_start(test->process_name, &processes[process_count]) == -1) { - snprintf((char*)&errmsg, sizeof(errmsg), "process `%s` failed to start.", - test->process_name); - goto finalize; + rewind_cursor(); + if (!benchmark_output) { + log_progress(total, passed, failed, task->task_name); + } + + if (run_test(task->task_name, timeout, benchmark_output) == 0) { + passed++; + } else { + failed++; + } } - main_process = &processes[process_count]; - process_count++; - /* Wait for the main process to terminate. */ - result = process_wait(main_process, 1, timeout); + rewind_cursor(); + + if (!benchmark_output) { + log_progress(total, passed, failed, "Done.\n"); + } + + return 0; +} + + +int run_test(const char* test, int timeout, int benchmark_output) { + char errmsg[1024] = "no error"; + process_info_t processes[1024]; + process_info_t *main_proc; + task_entry_t* task; + int process_count; + int result; + int status; + int i; + + status = 255; + process_count = 0; + + /* If it's a helper the user asks for, start it directly. */ + for (task = TASKS; task->main; task++) { + if (task->is_helper && strcmp(test, task->process_name) == 0) { + return task->main(); + } + } + + /* Start the helpers first. */ + for (task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) != 0) { + continue; + } + + /* Skip the test itself. */ + if (!task->is_helper) { + continue; + } + + if (process_start(task->task_name, + task->process_name, + &processes[process_count]) == -1) { + snprintf(errmsg, + sizeof errmsg, + "Process `%s` failed to start.", + task->process_name); + goto out; + } + + process_count++; + } + + /* Give the helpers time to settle. Race-y, fix this. */ + uv_sleep(250); + + /* Now start the test itself. */ + for (main_proc = NULL, task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) != 0) { + continue; + } + + if (task->is_helper) { + continue; + } + + if (process_start(task->task_name, + task->process_name, + &processes[process_count]) == -1) { + snprintf(errmsg, + sizeof errmsg, + "Process `%s` failed to start.", + task->process_name); + goto out; + } + + main_proc = &processes[process_count]; + process_count++; + break; + } + + if (main_proc == NULL) { + snprintf(errmsg, + sizeof errmsg, + "No test with that name: %s", + test); + goto out; + } + + result = process_wait(main_proc, 1, timeout); if (result == -1) { FATAL("process_wait failed"); } else if (result == -2) { - snprintf((char*)&errmsg, sizeof(errmsg), "timeout."); - goto finalize; + /* Don't have to clean up the process, process_wait() has killed it. */ + snprintf(errmsg, + sizeof errmsg, + "timeout"); + goto out; } - /* Reap the main process. */ - result = process_reap(main_process); - if (result != 0) { - snprintf((char*)&errmsg, sizeof(errmsg), "exit code %d.", result); - goto finalize; + status = process_reap(main_proc); + if (status != 0) { + snprintf(errmsg, + sizeof errmsg, + "exit code %d", + status); + goto out; } - /* Yes! did it. */ - success = 1; + if (benchmark_output) { + /* Give the helpers time to clean up their act. */ + uv_sleep(1000); + } -finalize: - /* Kill all (helper) processes that are still running. */ - for (i = 0; i < process_count; i++) { - /* If terminate fails the process is probably already closed. */ +out: + /* Reap running processes except the main process, it's already dead. */ + for (i = 0; i < process_count - 1; i++) { process_terminate(&processes[i]); } - /* Wait until all processes have really terminated. */ - if (process_wait((process_info_t*)&processes, process_count, -1) < 0) { + if (process_wait(processes, process_count - 1, -1) < 0) { FATAL("process_wait failed"); } /* Show error and output from processes if the test failed. */ - if (!success) { - LOGF("\n`%s` failed: %s\n", test->task_name, errmsg); + if (status != 0) { + LOGF("\n`%s` failed: %s\n", test, errmsg); for (i = 0; i < process_count; i++) { switch (process_output_size(&processes[i])) { @@ -145,13 +216,13 @@ finalize: /* In benchmark mode show concise output from the main process. */ } else if (benchmark_output) { - switch (process_output_size(main_process)) { + switch (process_output_size(main_proc)) { case -1: - LOGF("%s: (unavailabe)\n", test->task_name); + LOGF("%s: (unavailabe)\n", test); break; case 0: - LOGF("%s: (no output)\n", test->task_name); + LOGF("%s: (no output)\n", test); break; default: @@ -167,5 +238,74 @@ finalize: process_cleanup(&processes[i]); } - return success; + return status; +} + + +/* Returns the status code of the task part + * or 255 if no matching task was not found. + */ +int run_test_part(const char* test, const char* part) { + task_entry_t* task; + + for (task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) == 0 + && strcmp(part, task->process_name) == 0) { + return task->main(); + } + } + + LOGF("No test part with that name: %s:%s\n", test, part); + return 255; +} + + +static int compare_task(const void* va, const void* vb) { + const task_entry_t* a = va; + const task_entry_t* b = vb; + return strcmp(a->task_name, b->task_name); +} + + +static int find_helpers(const task_entry_t* task, const task_entry_t** helpers) { + const task_entry_t* helper; + int n_helpers; + + for (n_helpers = 0, helper = TASKS; helper->main; helper++) { + if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) { + *helpers++ = helper; + n_helpers++; + } + } + + return n_helpers; +} + + +void print_tests(FILE* stream) { + const task_entry_t* helpers[1024]; + const task_entry_t* task; + int n_helpers; + int n_tasks; + int i; + + for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++); + qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task); + + for (task = TASKS; task->main; task++) { + if (task->is_helper) { + continue; + } + + n_helpers = find_helpers(task, helpers); + if (n_helpers) { + printf("%-25s (helpers:", task->task_name); + for (i = 0; i < n_helpers; i++) { + printf(" %s", helpers[i]->process_name); + } + printf(")\n"); + } else { + printf("%s\n", task->task_name); + } + } } diff --git a/src/rt/libuv/test/runner.h b/src/rt/libuv/test/runner.h index 11d498020a1..3b93ffe991b 100644 --- a/src/rt/libuv/test/runner.h +++ b/src/rt/libuv/test/runner.h @@ -22,6 +22,8 @@ #ifndef RUNNER_H_ #define RUNNER_H_ +#include /* FILE */ + /* * The maximum number of processes (main + helpers) that a test / benchmark @@ -41,13 +43,6 @@ typedef struct { } task_entry_t, bench_entry_t; -/* Runs an individual task; returns 1 if the test succeeded, 0 if it failed. */ -/* If the test fails it prints diagnostic information. */ -/* If benchmark_output is nonzero, the output from the main process is -/* always shown. */ -int run_task(task_entry_t *test, int timeout, int benchmark_output); - - /* * Macros used by test-list.h and benchmark-list.h. */ @@ -95,13 +90,26 @@ extern char executable_path[PATHMAX]; /* The array that is filled by test-list.h or benchmark-list.h */ extern task_entry_t TASKS[]; -/* Start a specific process declared by TEST_ENTRY or TEST_HELPER. */ -/* Returns the exit code of the specific process. */ -int run_task(task_entry_t *test, int timeout, int benchmark_output); +/* + * Run all tests. + */ +int run_tests(int timeout, int benchmark_output); -/* Start a specific process declared by TEST_ENTRY or TEST_HELPER. */ -/* Returns the exit code of the specific process. */ -int run_process(char* name); +/* + * Run a single test. Starts up any helpers. + */ +int run_test(const char* test, int timeout, int benchmark_output); + +/* + * Run a test part, i.e. the test or one of its helpers. + */ +int run_test_part(const char* test, const char* part); + + +/* + * Print tests in sorted order to `stream`. Used by `./run-tests --list`. + */ +void print_tests(FILE* stream); /* @@ -113,9 +121,9 @@ int run_process(char* name); /* Do platform-specific initialization. */ void platform_init(); -/* Invoke "arv[0] test-name". Store process info in *p. */ +/* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ /* Make sure that all stdio output of the processes is buffered up. */ -int process_start(char *name, process_info_t *p); +int process_start(char *name, char* part, process_info_t *p); /* Wait for all `n` processes in `vec` to terminate. */ /* Time out after `timeout` msec, or never if timeout == -1 */ diff --git a/src/rt/libuv/test/task.h b/src/rt/libuv/test/task.h index 8d9a1e8e0df..76c6903311e 100644 --- a/src/rt/libuv/test/task.h +++ b/src/rt/libuv/test/task.h @@ -30,10 +30,31 @@ #define TEST_PORT 9123 #define TEST_PORT_2 9124 +#ifdef _WIN32 +# define TEST_PIPENAME "\\\\.\\pipe\\uv-test" +# define TEST_PIPENAME_2 "\\\\.\\pipe\\uv-test2" +#else +# define TEST_PIPENAME "/tmp/uv-test-sock" +# define TEST_PIPENAME_2 "/tmp/uv-test-sock2" +#endif + +typedef enum { + TCP = 0, + PIPE +} stream_type; /* Log to stderr. */ -#define LOG(...) fprintf(stderr, "%s", __VA_ARGS__) -#define LOGF(...) fprintf(stderr, __VA_ARGS__) +#define LOG(...) \ + do { \ + fprintf(stderr, "%s", __VA_ARGS__); \ + fflush(stderr); \ + } while (0) + +#define LOGF(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } while (0) /* Die with fatal error. */ #define FATAL(msg) \ @@ -43,6 +64,7 @@ __FILE__, \ __LINE__, \ msg); \ + fflush(stderr); \ abort(); \ } while (0) diff --git a/src/rt/libuv/test/test-async.c b/src/rt/libuv/test/test-async.c index a9933fdcb14..3d8415183d1 100644 --- a/src/rt/libuv/test/test-async.c +++ b/src/rt/libuv/test/test-async.c @@ -47,8 +47,6 @@ static uintptr_t thread3_id = 0; /* Thread 1 makes sure that async1_cb_called reaches 3 before exiting. */ void thread1_entry(void *arg) { - int state = 0; - uv_sleep(50); while (1) { @@ -148,8 +146,6 @@ static void async2_cb(uv_handle_t* handle, int status) { static void prepare_cb(uv_prepare_t* handle, int status) { - int r; - ASSERT(handle == &prepare_handle); ASSERT(status == 0); @@ -172,8 +168,7 @@ static void prepare_cb(uv_prepare_t* handle, int status) { #endif case 1: - r = uv_close((uv_handle_t*)handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)handle, close_cb); break; default: @@ -187,14 +182,12 @@ static void prepare_cb(uv_prepare_t* handle, int status) { TEST_IMPL(async) { int r; - uv_init(); - - r = uv_prepare_init(&prepare_handle); + r = uv_prepare_init(uv_default_loop(), &prepare_handle); ASSERT(r == 0); r = uv_prepare_start(&prepare_handle, prepare_cb); ASSERT(r == 0); - r = uv_async_init(&async1_handle, async1_cb); + r = uv_async_init(uv_default_loop(), &async1_handle, async1_cb); ASSERT(r == 0); #if 0 @@ -202,7 +195,7 @@ TEST_IMPL(async) { ASSERT(r == 0); #endif - r = uv_run(); + r = uv_run(uv_default_loop()); ASSERT(r == 0); r = uv_wait_thread(thread1_id); diff --git a/src/rt/libuv/test/test-callback-stack.c b/src/rt/libuv/test/test-callback-stack.c index 5b12c8b9b42..4af63648861 100644 --- a/src/rt/libuv/test/test-callback-stack.c +++ b/src/rt/libuv/test/test-callback-stack.c @@ -32,7 +32,9 @@ static const char MESSAGE[] = "Failure is for the weak. Everyone dies alone."; static uv_tcp_t client; static uv_timer_t timer; -static uv_req_t connect_req, write_req, shutdown_req; +static uv_connect_t connect_req; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; static int nested = 0; static int close_cb_called = 0; @@ -43,7 +45,7 @@ static int bytes_received = 0; static int shutdown_cb_called = 0; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.len = size; buf.base = (char*) malloc(size); @@ -59,7 +61,7 @@ static void close_cb(uv_handle_t* handle) { } -static void shutdown_cb(uv_req_t* req, int status) { +static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(status == 0); ASSERT(nested == 0 && "shutdown_cb must be called from a fresh stack"); @@ -74,16 +76,14 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { free(buf.base); if (nread == 0) { - ASSERT(uv_last_error().code == UV_EAGAIN); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EAGAIN); return; } else if (nread == -1) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); nested++; - if (uv_close((uv_handle_t*)tcp, close_cb)) { - FATAL("uv_close failed"); - } + uv_close((uv_handle_t*)tcp, close_cb); nested--; return; @@ -97,11 +97,10 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { /* from a fresh stack. */ if (bytes_received == sizeof MESSAGE) { nested++; - uv_req_init(&shutdown_req, (uv_handle_t*)tcp, (void *(*)(void *))shutdown_cb); puts("Shutdown"); - if (uv_shutdown(&shutdown_req)) { + if (uv_shutdown(&shutdown_req, (uv_stream_t*)tcp, shutdown_cb)) { FATAL("uv_shutdown failed"); } nested--; @@ -110,8 +109,6 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { static void timer_cb(uv_timer_t* handle, int status) { - int r; - ASSERT(handle == &timer); ASSERT(status == 0); ASSERT(nested == 0 && "timer_cb must be called from a fresh stack"); @@ -126,12 +123,11 @@ static void timer_cb(uv_timer_t* handle, int status) { timer_cb_called++; - r = uv_close((uv_handle_t*)handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)handle, close_cb); } -static void write_cb(uv_req_t* req, int status) { +static void write_cb(uv_write_t* req, int status) { int r; ASSERT(status == 0); @@ -144,7 +140,7 @@ static void write_cb(uv_req_t* req, int status) { /* back to our receive buffer when we start reading. This maximizes the */ /* tempation for the backend to use dirty stack for calling read_cb. */ nested++; - r = uv_timer_init(&timer); + r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 500, 0); ASSERT(r == 0); @@ -154,7 +150,7 @@ static void write_cb(uv_req_t* req, int status) { } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { uv_buf_t buf; puts("Connected. Write some data to echo server..."); @@ -167,9 +163,7 @@ static void connect_cb(uv_req_t* req, int status) { buf.base = (char*) &MESSAGE; buf.len = sizeof MESSAGE; - uv_req_init(&write_req, req->handle, (void *(*)(void *))write_cb); - - if (uv_write(&write_req, &buf, 1)) { + if (uv_write(&write_req, (uv_stream_t*)req->handle, &buf, 1, write_cb)) { FATAL("uv_write failed"); } @@ -182,24 +176,20 @@ static void connect_cb(uv_req_t* req, int status) { TEST_IMPL(callback_stack) { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); - uv_init(); - - if (uv_tcp_init(&client)) { + if (uv_tcp_init(uv_default_loop(), &client)) { FATAL("uv_tcp_init failed"); } puts("Connecting..."); nested++; - uv_req_init(&connect_req, (uv_handle_t*)&client, - (void *(*)(void *))connect_cb); - if (uv_tcp_connect(&connect_req, addr)) { + if (uv_tcp_connect(&connect_req, &client, addr, connect_cb)) { FATAL("uv_tcp_connect failed"); } nested--; - uv_run(); + uv_run(uv_default_loop()); ASSERT(nested == 0); ASSERT(connect_cb_called == 1 && "connect_cb must be called exactly once"); diff --git a/src/rt/libuv/test/test-connection-fail.c b/src/rt/libuv/test/test-connection-fail.c index 9fc3f0ba6eb..2762aa285a9 100644 --- a/src/rt/libuv/test/test-connection-fail.c +++ b/src/rt/libuv/test/test-connection-fail.c @@ -27,7 +27,7 @@ static uv_tcp_t tcp; -static uv_req_t req; +static uv_connect_t req; static int connect_cb_calls; static int close_cb_calls; @@ -66,20 +66,20 @@ static void timer_cb(uv_timer_t* handle, int status) { } -static void on_connect_with_close(uv_req_t *req, int status) { - ASSERT(&tcp == (uv_tcp_t*) req->handle); +static void on_connect_with_close(uv_connect_t *req, int status) { + ASSERT((uv_stream_t*) &tcp == req->handle); ASSERT(status == -1); - ASSERT(uv_last_error().code == UV_ECONNREFUSED); + ASSERT(uv_last_error(uv_default_loop()).code == UV_ECONNREFUSED); connect_cb_calls++; ASSERT(close_cb_calls == 0); - uv_close(req->handle, on_close); + uv_close((uv_handle_t*)req->handle, on_close); } -static void on_connect_without_close(uv_req_t *req, int status) { +static void on_connect_without_close(uv_connect_t *req, int status) { ASSERT(status == -1); - ASSERT(uv_last_error().code == UV_ECONNREFUSED); + ASSERT(uv_last_error(uv_default_loop()).code == UV_ECONNREFUSED); connect_cb_calls++; uv_timer_start(&timer, timer_cb, 100, 0); @@ -98,18 +98,16 @@ void connection_fail(uv_connect_cb connect_cb) { server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&tcp); + r = uv_tcp_init(uv_default_loop(), &tcp); ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ - uv_req_init(&req, (uv_handle_t*)&tcp, (void *(*)(void *))connect_cb); - uv_tcp_bind(&tcp, client_addr); - r = uv_tcp_connect(&req, server_addr); + r = uv_tcp_connect(&req, &tcp, server_addr, connect_cb); ASSERT(!r); - uv_run(); + uv_run(uv_default_loop()); ASSERT(connect_cb_calls == 1); ASSERT(close_cb_calls == 1); @@ -121,8 +119,6 @@ void connection_fail(uv_connect_cb connect_cb) { * expect an error. */ TEST_IMPL(connection_fail) { - uv_init(); - connection_fail(on_connect_with_close); ASSERT(timer_close_cb_calls == 0); @@ -138,9 +134,10 @@ TEST_IMPL(connection_fail) { * attempt. */ TEST_IMPL(connection_fail_doesnt_auto_close) { - uv_init(); + int r; - uv_timer_init(&timer); + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); connection_fail(on_connect_without_close); diff --git a/src/rt/libuv/test/test-delayed-accept.c b/src/rt/libuv/test/test-delayed-accept.c index eabf4818959..78531f68067 100644 --- a/src/rt/libuv/test/test-delayed-accept.c +++ b/src/rt/libuv/test/test-delayed-accept.c @@ -24,15 +24,13 @@ #include #include -static char BUFFER[1024]; - static int connection_cb_called = 0; static int do_accept_called = 0; static int close_cb_called = 0; static int connect_cb_called = 0; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -52,43 +50,41 @@ static void close_cb(uv_handle_t* handle) { static void do_accept(uv_timer_t* timer_handle, int status) { uv_tcp_t* server; uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle); + uint64_t tcpcnt; int r; - int tcpcnt; ASSERT(timer_handle != NULL); ASSERT(status == 0); ASSERT(accepted_handle != NULL); - uv_tcp_init(accepted_handle); - - /* Test to that uv_counters()->tcp_init does not increase across the uv_accept. */ - tcpcnt = uv_counters()->tcp_init; - - server = (uv_tcp_t*)timer_handle->data; - r = uv_accept((uv_handle_t*)server, (uv_stream_t*)accepted_handle); + r = uv_tcp_init(uv_default_loop(), accepted_handle); ASSERT(r == 0); - ASSERT(uv_counters()->tcp_init == tcpcnt); + /* Test to that uv_default_loop()->counters.tcp_init does not increase across the uv_accept. */ + tcpcnt = uv_default_loop()->counters.tcp_init; + + server = (uv_tcp_t*)timer_handle->data; + r = uv_accept((uv_stream_t*)server, (uv_stream_t*)accepted_handle); + ASSERT(r == 0); + + ASSERT(uv_default_loop()->counters.tcp_init == tcpcnt); do_accept_called++; /* Immediately close the accepted handle. */ - r = uv_close((uv_handle_t*)accepted_handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)accepted_handle, close_cb); /* After accepting the two clients close the server handle */ if (do_accept_called == 2) { - r = uv_close((uv_handle_t*)server, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)server, close_cb); } /* Dispose the timer. */ - r = uv_close((uv_handle_t*)timer_handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)timer_handle, close_cb); } -static void connection_cb(uv_handle_t* tcp, int status) { +static void connection_cb(uv_stream_t* tcp, int status) { int r; uv_timer_t* timer_handle; @@ -98,7 +94,7 @@ static void connection_cb(uv_handle_t* tcp, int status) { ASSERT(timer_handle != NULL); /* Accept the client after 1 second */ - r = uv_timer_init(timer_handle); + r = uv_timer_init(uv_default_loop(), timer_handle); ASSERT(r == 0); timer_handle->data = tcp; @@ -117,15 +113,15 @@ static void start_server() { ASSERT(server != NULL); - r = uv_tcp_init(server); + r = uv_tcp_init(uv_default_loop(), server); ASSERT(r == 0); - ASSERT(uv_counters()->tcp_init == 1); - ASSERT(uv_counters()->handle_init == 1); + ASSERT(uv_default_loop()->counters.tcp_init == 1); + ASSERT(uv_default_loop()->counters.handle_init == 1); r = uv_tcp_bind(server, addr); ASSERT(r == 0); - r = uv_tcp_listen(server, 128, connection_cb); + r = uv_listen((uv_stream_t*)server, 128, connection_cb); ASSERT(r == 0); } @@ -139,17 +135,17 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { if (nread != -1) { ASSERT(nread == 0); - ASSERT(uv_last_error().code == UV_EAGAIN); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EAGAIN); } else { ASSERT(tcp != NULL); ASSERT(nread == -1); - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); uv_close((uv_handle_t*)tcp, close_cb); } } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { int r; ASSERT(req != NULL); @@ -169,31 +165,28 @@ static void connect_cb(uv_req_t* req, int status) { static void client_connect() { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof *client); - uv_req_t* connect_req = (uv_req_t*)malloc(sizeof *connect_req); + uv_connect_t* connect_req = malloc(sizeof *connect_req); int r; ASSERT(client != NULL); ASSERT(connect_req != NULL); - r = uv_tcp_init(client); + r = uv_tcp_init(uv_default_loop(), client); ASSERT(r == 0); - uv_req_init(connect_req, (uv_handle_t*)client, (void *(*)(void *))connect_cb); - r = uv_tcp_connect(connect_req, addr); + r = uv_tcp_connect(connect_req, client, addr, connect_cb); ASSERT(r == 0); } TEST_IMPL(delayed_accept) { - uv_init(); - start_server(); client_connect(); client_connect(); - uv_run(); + uv_run(uv_default_loop()); ASSERT(connection_cb_called == 2); ASSERT(do_accept_called == 2); diff --git a/src/rt/libuv/test/test-fs-event.c b/src/rt/libuv/test/test-fs-event.c new file mode 100644 index 00000000000..d249ec6692c --- /dev/null +++ b/src/rt/libuv/test/test-fs-event.c @@ -0,0 +1,217 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include + +uv_fs_event_t fs_event; +uv_timer_t timer; +int timer_cb_called; +int close_cb_called; +int fs_event_cb_called; + +static void create_dir(uv_loop_t* loop, const char* name) { + int r; + uv_fs_t req; + r = uv_fs_mkdir(loop, &req, name, 0755, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void create_file(uv_loop_t* loop, const char* name) { + int r; + uv_file file; + uv_fs_t req; + + r = uv_fs_open(loop, &req, name, O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + file = r; + uv_fs_req_cleanup(&req); + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void touch_file(uv_loop_t* loop, const char* name) { + int r; + uv_file file; + uv_fs_t req; + + r = uv_fs_open(loop, &req, name, O_RDWR, 0, NULL); + ASSERT(r != -1); + file = r; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, "foo", 4, -1, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&req); + + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&req); +} + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + +static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_RENAME); + ASSERT(strcmp(filename, "file1") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + ASSERT(strcmp(filename, "file2") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, + const char* filename, int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + ASSERT(strcmp(filename, "watch_file") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void timer_cb_dir(uv_timer_t* handle, int status) { + ++timer_cb_called; + create_file(handle->loop, "watch_dir/file1"); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void timer_cb_file(uv_timer_t* handle, int status) { + ++timer_cb_called; + + if (timer_cb_called == 1) { + touch_file(handle->loop, "watch_dir/file1"); + } else { + touch_file(handle->loop, "watch_dir/file2"); + uv_close((uv_handle_t*)handle, close_cb); + } +} + +TEST_IMPL(fs_event_watch_dir) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + create_dir(loop, "watch_dir"); + + r = uv_fs_event_init(loop, &fs_event, "watch_dir", fs_event_cb_dir); + ASSERT(r != -1); + r = uv_timer_init(loop, &timer); + ASSERT(r != -1); + r = uv_timer_start(&timer, timer_cb_dir, 100, 0); + ASSERT(r != -1); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + r = uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + + return 0; +} + +TEST_IMPL(fs_event_watch_file) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + create_dir(loop, "watch_dir"); + create_file(loop, "watch_dir/file1"); + create_file(loop, "watch_dir/file2"); + + r = uv_fs_event_init(loop, &fs_event, "watch_dir/file2", fs_event_cb_file); + ASSERT(r != -1); + r = uv_timer_init(loop, &timer); + ASSERT(r != -1); + r = uv_timer_start(&timer, timer_cb_file, 100, 100); + ASSERT(r != -1); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(timer_cb_called == 2); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + r = uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + + return 0; +} + +TEST_IMPL(fs_event_watch_file_current_dir) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_file", NULL); + create_file(loop, "watch_file"); + + r = uv_fs_event_init(loop, &fs_event, "watch_file", + fs_event_cb_file_current_dir); + ASSERT(r != -1); + + touch_file(loop, "watch_file"); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(close_cb_called == 1); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_file", NULL); + return 0; +} diff --git a/src/rt/libuv/test/test-fs.c b/src/rt/libuv/test/test-fs.c new file mode 100644 index 00000000000..fbe0396bf84 --- /dev/null +++ b/src/rt/libuv/test/test-fs.c @@ -0,0 +1,1247 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* FIXME we shouldnt need to branch in this file */ +#define UNIX (defined(__unix__) || defined(__POSIX__) || defined(__APPLE__)) + +#include "uv.h" +#include "task.h" + +#include +#include /* memset */ +#include +#include + + +#if UNIX +#include /* unlink, rmdir, etc. */ +#else +# include +# include +# define unlink _unlink +# define rmdir _rmdir +# define stat _stati64 +# define open _open +# define write _write +# define lseek _lseek +# define close _close +#endif + + +typedef struct { + const char* path; + double atime; + double mtime; +} utime_check_t; + + +static int close_cb_count; +static int create_cb_count; +static int open_cb_count; +static int read_cb_count; +static int write_cb_count; +static int unlink_cb_count; +static int mkdir_cb_count; +static int rmdir_cb_count; +static int readdir_cb_count; +static int stat_cb_count; +static int rename_cb_count; +static int fsync_cb_count; +static int fdatasync_cb_count; +static int ftruncate_cb_count; +static int sendfile_cb_count; +static int fstat_cb_count; +static int chmod_cb_count; +static int fchmod_cb_count; +static int chown_cb_count; +static int fchown_cb_count; +static int link_cb_count; +static int symlink_cb_count; +static int readlink_cb_count; +static int utime_cb_count; +static int futime_cb_count; + +static uv_loop_t* loop; + +static uv_fs_t open_req1; +static uv_fs_t open_req2; +static uv_fs_t read_req; +static uv_fs_t write_req; +static uv_fs_t unlink_req; +static uv_fs_t close_req; +static uv_fs_t mkdir_req; +static uv_fs_t rmdir_req; +static uv_fs_t readdir_req; +static uv_fs_t stat_req; +static uv_fs_t rename_req; +static uv_fs_t fsync_req; +static uv_fs_t fdatasync_req; +static uv_fs_t ftruncate_req; +static uv_fs_t sendfile_req; +static uv_fs_t utime_req; +static uv_fs_t futime_req; + +static char buf[32]; +static char test_buf[] = "test-buffer\n"; + + +void check_permission(const char* filename, int mode) { + int r; + uv_fs_t req; + struct stat* s; + + r = uv_fs_stat(uv_default_loop(), &req, filename, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + + s = req.ptr; +#ifdef _WIN32 + /* + * On Windows, chmod can only modify S_IWUSR (_S_IWRITE) bit, + * so only testing for the specified flags. + */ + ASSERT((s->st_mode & 0777) & mode); +#else + ASSERT((s->st_mode & 0777) == mode); +#endif + + uv_fs_req_cleanup(&req); +} + + +static void link_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_LINK); + ASSERT(req->result == 0); + link_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void symlink_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_SYMLINK); + ASSERT(req->result == 0); + symlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void readlink_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_READLINK); + ASSERT(req->result == 0); + ASSERT(strcmp(req->ptr, "test_file_symlink2") == 0); + readlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void fchmod_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_FCHMOD); + ASSERT(req->result == 0); + fchmod_cb_count++; + uv_fs_req_cleanup(req); + check_permission("test_file", (int)req->data); +} + + +static void chmod_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_CHMOD); + ASSERT(req->result == 0); + chmod_cb_count++; + uv_fs_req_cleanup(req); + check_permission("test_file", (int)req->data); +} + + +static void fchown_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_FCHOWN); + ASSERT(req->result == 0); + fchown_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void chown_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_CHOWN); + ASSERT(req->result == 0); + chown_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void unlink_cb(uv_fs_t* req) { + ASSERT(req == &unlink_req); + ASSERT(req->fs_type == UV_FS_UNLINK); + ASSERT(req->result != -1); + unlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void fstat_cb(uv_fs_t* req) { + struct stat* s = req->ptr; + ASSERT(req->fs_type == UV_FS_FSTAT); + ASSERT(req->result == 0); + ASSERT(s->st_size == sizeof(test_buf)); + uv_fs_req_cleanup(req); + fstat_cb_count++; +} + + +static void close_cb(uv_fs_t* req) { + int r; + ASSERT(req == &close_req); + ASSERT(req->fs_type == UV_FS_CLOSE); + ASSERT(req->result != -1); + close_cb_count++; + uv_fs_req_cleanup(req); + if (close_cb_count == 3) { + r = uv_fs_unlink(loop, &unlink_req, "test_file2", unlink_cb); + } +} + + +static void ftruncate_cb(uv_fs_t* req) { + int r; + ASSERT(req == &ftruncate_req); + ASSERT(req->fs_type == UV_FS_FTRUNCATE); + ASSERT(req->result != -1); + ftruncate_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); +} + + +static void read_cb(uv_fs_t* req) { + int r; + ASSERT(req == &read_req); + ASSERT(req->fs_type == UV_FS_READ); + ASSERT(req->result != -1); + read_cb_count++; + uv_fs_req_cleanup(req); + if (read_cb_count == 1) { + ASSERT(strcmp(buf, test_buf) == 0); + r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, + ftruncate_cb); + } else { + ASSERT(strcmp(buf, "test-bu") == 0); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); + } +} + + +static void open_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + if (req->result < 0) { + /* TODO get error with uv_last_error() */ + fprintf(stderr, "async open error: %d\n", req->errorno); + ASSERT(0); + } + open_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_file2\0", 11) == 0); + uv_fs_req_cleanup(req); + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + read_cb); +} + + +static void fsync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fsync_req); + ASSERT(req->fs_type == UV_FS_FSYNC); + ASSERT(req->result != -1); + fsync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); +} + + +static void fdatasync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fdatasync_req); + ASSERT(req->fs_type == UV_FS_FDATASYNC); + ASSERT(req->result != -1); + fdatasync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fsync(loop, &fsync_req, open_req1.result, fsync_cb); +} + + +static void write_cb(uv_fs_t* req) { + int r; + ASSERT(req == &write_req); + ASSERT(req->fs_type == UV_FS_WRITE); + ASSERT(req->result != -1); + write_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb); +} + + +static void create_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result != -1); + create_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_write(loop, &write_req, req->result, test_buf, sizeof(test_buf), + -1, write_cb); +} + + +static void rename_cb(uv_fs_t* req) { + ASSERT(req == &rename_req); + ASSERT(req->fs_type == UV_FS_RENAME); + ASSERT(req->result != -1); + rename_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void mkdir_cb(uv_fs_t* req) { + ASSERT(req == &mkdir_req); + ASSERT(req->fs_type == UV_FS_MKDIR); + ASSERT(req->result != -1); + mkdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); +} + + +static void rmdir_cb(uv_fs_t* req) { + ASSERT(req == &rmdir_req); + ASSERT(req->fs_type == UV_FS_RMDIR); + ASSERT(req->result != -1); + rmdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); +} + + +static void readdir_cb(uv_fs_t* req) { + ASSERT(req == &readdir_req); + ASSERT(req->fs_type == UV_FS_READDIR); + ASSERT(req->result == 2); + ASSERT(req->ptr); + ASSERT(memcmp(req->ptr, "file1\0file2\0", 12) == 0 + || memcmp(req->ptr, "file2\0file1\0", 12) == 0); + readdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void stat_cb(uv_fs_t* req) { + ASSERT(req == &stat_req); + ASSERT(req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT); + ASSERT(req->result != -1); + ASSERT(req->ptr); + stat_cb_count++; + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void sendfile_cb(uv_fs_t* req) { + ASSERT(req == &sendfile_req); + ASSERT(req->fs_type == UV_FS_SENDFILE); + ASSERT(req->result == 65546); + sendfile_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void open_noent_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->errorno == UV_ENOENT); + ASSERT(req->result == -1); + open_cb_count++; + uv_fs_req_cleanup(req); +} + + +TEST_IMPL(fs_file_noent) { + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "does_not_exist", O_RDONLY, 0, NULL); + ASSERT(r == -1); + ASSERT(req.result == -1); + ASSERT(uv_last_error(loop).code == UV_ENOENT); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "does_not_exist", O_RDONLY, 0, open_noent_cb); + ASSERT(r == 0); + + ASSERT(open_cb_count == 0); + uv_run(loop); + ASSERT(open_cb_count == 1); + + /* TODO add EACCES test */ + + return 0; +} + + +static void check_utime(const char* path, double atime, double mtime) { + struct stat* s; + uv_fs_t req; + int r; + + r = uv_fs_stat(loop, &req, path, NULL); + ASSERT(r == 0); + + ASSERT(req.result == 0); + s = req.ptr; + +#if _WIN32 + ASSERT(s->st_atime == atime); + ASSERT(s->st_mtime == mtime); +#elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) + ASSERT(s->st_atimespec.tv_sec == atime); + ASSERT(s->st_mtimespec.tv_sec == mtime); +#else + ASSERT(s->st_atim.tv_sec == atime); + ASSERT(s->st_mtim.tv_sec == mtime); +#endif + + uv_fs_req_cleanup(&req); +} + + +static void utime_cb(uv_fs_t* req) { + utime_check_t* c; + + ASSERT(req == &utime_req); + ASSERT(req->result == 0); + ASSERT(req->fs_type == UV_FS_UTIME); + + c = req->data; + check_utime(c->path, c->atime, c->mtime); + + uv_fs_req_cleanup(req); + utime_cb_count++; +} + + +static void futime_cb(uv_fs_t* req) { + utime_check_t* c; + + ASSERT(req == &futime_req); + ASSERT(req->result == 0); + ASSERT(req->fs_type == UV_FS_FUTIME); + + c = req->data; + check_utime(c->path, c->atime, c->mtime); + + uv_fs_req_cleanup(req); + futime_cb_count++; +} + + +TEST_IMPL(fs_file_async) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IREAD | S_IWRITE, create_cb); + ASSERT(r == 0); + uv_run(loop); + + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(fsync_cb_count == 1); + ASSERT(fdatasync_cb_count == 1); + ASSERT(close_cb_count == 1); + + r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", rename_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(close_cb_count == 1); + ASSERT(rename_cb_count == 1); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDWR, 0, open_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(open_cb_count == 1); + ASSERT(read_cb_count == 1); + ASSERT(close_cb_count == 2); + ASSERT(rename_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, open_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(open_cb_count == 2); + ASSERT(read_cb_count == 2); + ASSERT(close_cb_count == 3); + ASSERT(rename_cb_count == 1); + ASSERT(unlink_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_file_sync) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_write(loop, &write_req, open_req1.result, test_buf, + sizeof(test_buf), -1, NULL); + ASSERT(r != -1); + ASSERT(write_req.result != -1); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(loop, &open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + NULL); + ASSERT(r != -1); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, NULL); + ASSERT(r != -1); + ASSERT(ftruncate_req.result != -1); + uv_fs_req_cleanup(&ftruncate_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", NULL); + ASSERT(r != -1); + ASSERT(rename_req.result != -1); + uv_fs_req_cleanup(&rename_req); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + NULL); + ASSERT(r != -1); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, "test-bu") == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_unlink(loop, &unlink_req, "test_file2", NULL); + ASSERT(r != -1); + ASSERT(unlink_req.result != -1); + uv_fs_req_cleanup(&unlink_req); + + /* Cleanup */ + unlink("test_file"); + unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_async_dir) { + int r; + + /* Setup */ + unlink("test_dir/file1"); + unlink("test_dir/file2"); + rmdir("test_dir"); + + loop = uv_default_loop(); + + r = uv_fs_mkdir(loop, &mkdir_req, "test_dir", 0755, mkdir_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(mkdir_cb_count == 1); + + /* Create 2 files synchronously. */ + r = uv_fs_open(loop, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(loop, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_readdir(loop, &readdir_req, "test_dir", 0, readdir_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(readdir_cb_count == 1); + + /* sync uv_fs_readdir */ + r = uv_fs_readdir(loop, &readdir_req, "test_dir", 0, NULL); + ASSERT(r == 2); + ASSERT(readdir_req.result == 2); + ASSERT(readdir_req.ptr); + ASSERT(memcmp(readdir_req.ptr, "file1\0file2\0", 12) == 0 + || memcmp(readdir_req.ptr, "file2\0file1\0", 12) == 0); + uv_fs_req_cleanup(&readdir_req); + ASSERT(!readdir_req.ptr); + + r = uv_fs_stat(loop, &stat_req, "test_dir", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + r = uv_fs_stat(loop, &stat_req, "test_dir\\", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + r = uv_fs_lstat(loop, &stat_req, "test_dir", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + r = uv_fs_lstat(loop, &stat_req, "test_dir\\", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + ASSERT(stat_cb_count == 4); + + r = uv_fs_unlink(loop, &unlink_req, "test_dir/file1", unlink_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(unlink_cb_count == 1); + + r = uv_fs_unlink(loop, &unlink_req, "test_dir/file2", unlink_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(unlink_cb_count == 2); + + r = uv_fs_rmdir(loop, &rmdir_req, "test_dir", rmdir_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(rmdir_cb_count == 1); + + /* Cleanup */ + unlink("test_dir/file1"); + unlink("test_dir/file2"); + rmdir("test_dir"); + + return 0; +} + + +TEST_IMPL(fs_async_sendfile) { + int f, r; + struct stat s1, s2; + + loop = uv_default_loop(); + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + f = open("test_file", O_WRONLY | O_CREAT, S_IWRITE | S_IREAD); + ASSERT(f != -1); + + r = write(f, "begin\n", 6); + ASSERT(r == 6); + + r = lseek(f, 65536, SEEK_CUR); + ASSERT(r == 65542); + + r = write(f, "end\n", 4); + ASSERT(r != -1); + + r = close(f); + ASSERT(r == 0); + + /* Test starts here. */ + r = uv_fs_open(loop, &open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_open(loop, &open_req2, "test_file2", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(open_req2.result != -1); + uv_fs_req_cleanup(&open_req2); + + r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, + 0, 131072, sendfile_cb); + ASSERT(r == 0); + uv_run(loop); + + ASSERT(sendfile_cb_count == 1); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + r = uv_fs_close(loop, &close_req, open_req2.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + stat("test_file", &s1); + stat("test_file2", &s2); + ASSERT(65546 == s2.st_size && s1.st_size == s2.st_size); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_fstat) { + int r; + uv_fs_t req; + uv_file file; + struct stat* s; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + r = uv_fs_fstat(loop, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + s = req.ptr; + ASSERT(s->st_size == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + /* Now do the uv_fs_fstat call asynchronously */ + r = uv_fs_fstat(loop, &req, file, fstat_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(fstat_cb_count == 1); + + + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + + return 0; +} + + +TEST_IMPL(fs_chmod) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + +#ifndef _WIN32 + /* Make the file write-only */ + r = uv_fs_chmod(loop, &req, "test_file", 0200, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0200); +#endif + + /* Make the file read-only */ + r = uv_fs_chmod(loop, &req, "test_file", 0400, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0400); + + /* Make the file read+write with sync uv_fs_fchmod */ + r = uv_fs_fchmod(loop, &req, file, 0600, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0600); + +#ifndef _WIN32 + /* async chmod */ + req.data = (void*)0200; + r = uv_fs_chmod(loop, &req, "test_file", 0200, chmod_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(chmod_cb_count == 1); + chmod_cb_count = 0; /* reset for the next test */ +#endif + + /* async chmod */ + req.data = (void*)0400; + r = uv_fs_chmod(loop, &req, "test_file", 0400, chmod_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(chmod_cb_count == 1); + + /* async fchmod */ + req.data = (void*)0600; + r = uv_fs_fchmod(loop, &req, file, 0600, fchmod_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(fchmod_cb_count == 1); + + close(file); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + + return 0; +} + + +TEST_IMPL(fs_chown) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + /* sync chown */ + r = uv_fs_chown(loop, &req, "test_file", -1, -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* sync fchown */ + r = uv_fs_fchown(loop, &req, file, -1, -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* async chown */ + r = uv_fs_chown(loop, &req, "test_file", -1, -1, chown_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(chown_cb_count == 1); + + /* async fchown */ + r = uv_fs_fchown(loop, &req, file, -1, -1, fchown_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(fchown_cb_count == 1); + + close(file); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + + return 0; +} + + +TEST_IMPL(fs_link) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync link */ + r = uv_fs_link(loop, &req, "test_file", "test_file_link", NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "test_file_link", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* async link */ + r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(link_cb_count == 1); + + r = uv_fs_open(loop, &req, "test_file_link2", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + return 0; +} + + +TEST_IMPL(fs_symlink) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink2"); + unlink("test_file_symlink_symlink"); + unlink("test_file_symlink2_symlink"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync symlink */ + r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink", 0, NULL); +#ifdef _WIN32 + if (r == -1) { + if (req.errorno == ENOSYS) { + /* + * Windows doesn't support symlinks on older versions. + * We just pass the test and bail out early if we get ENOTSUP. + */ + return 0; + } else if (uv_last_error(loop).sys_errno_ == ERROR_PRIVILEGE_NOT_HELD) { + /* + * Creating a symlink is only allowed when running elevated. + * We pass the test and bail out early if we get ERROR_PRIVILEGE_NOT_HELD. + */ + return 0; + } + } +#endif + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "test_file_symlink", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + r = uv_fs_symlink(loop, &req, "test_file_symlink", "test_file_symlink_symlink", 0, NULL); + ASSERT(r != -1); + r = uv_fs_readlink(loop, &req, "test_file_symlink_symlink", NULL); + ASSERT(r != -1); + ASSERT(strcmp(req.ptr, "test_file_symlink") == 0); + uv_fs_req_cleanup(&req); + + /* async link */ + r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink2", 0, symlink_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(symlink_cb_count == 1); + + r = uv_fs_open(loop, &req, "test_file_symlink2", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + r = uv_fs_symlink(loop, &req, "test_file_symlink2", "test_file_symlink2_symlink", 0, NULL); + ASSERT(r != -1); + r = uv_fs_readlink(loop, &req, "test_file_symlink2_symlink", readlink_cb); + ASSERT(r != -1); + uv_run(loop); + ASSERT(readlink_cb_count == 1); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink_symlink"); + unlink("test_file_symlink2"); + unlink("test_file_symlink2_symlink"); + + return 0; +} + + +TEST_IMPL(fs_utime) { + utime_check_t checkme; + const char* path = "."; + double atime; + double mtime; + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ + + r = uv_fs_utime(loop, &req, path, atime, mtime, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_stat(loop, &req, path, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + check_utime(path, atime, mtime); + uv_fs_req_cleanup(&req); + + atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ + checkme.path = path; + checkme.atime = atime; + checkme.mtime = mtime; + + /* async utime */ + utime_req.data = &checkme; + r = uv_fs_utime(loop, &utime_req, path, atime, mtime, utime_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(utime_cb_count == 1); + + return 0; +} + + +TEST_IMPL(fs_futime) { + utime_check_t checkme; + const char* path = "."; + double atime; + double mtime; + uv_file file; + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ + + r = uv_fs_open(loop, &req, path, O_RDONLY, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; /* FIXME probably not how it's supposed to be used */ + uv_fs_req_cleanup(&req); + + r = uv_fs_futime(loop, &req, file, atime, mtime, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_stat(loop, &req, path, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + check_utime(path, atime, mtime); + uv_fs_req_cleanup(&req); + + atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ + + checkme.atime = atime; + checkme.mtime = mtime; + checkme.path = path; + + /* async futime */ + futime_req.data = &checkme; + r = uv_fs_futime(loop, &futime_req, file, atime, mtime, futime_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(futime_cb_count == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-get-currentexe.c b/src/rt/libuv/test/test-get-currentexe.c index 1dc85565590..9dd267691c0 100644 --- a/src/rt/libuv/test/test-get-currentexe.c +++ b/src/rt/libuv/test/test-get-currentexe.c @@ -30,15 +30,25 @@ TEST_IMPL(get_currentexe) { char buffer[PATHMAX]; size_t size; char* match; + char* path; int r; size = sizeof(buffer) / sizeof(buffer[0]); r = uv_exepath(buffer, &size); ASSERT(!r); - match = strstr(buffer, executable_path); + /* uv_exepath can return an absolute path on darwin, so if the test runner + * was run with a relative prefix of "./", we need to strip that prefix off + * executable_path or we'll fail. */ + if (executable_path[0] == '.' && executable_path[1] == '/') { + path = executable_path + 2; + } else { + path = executable_path; + } + + match = strstr(buffer, path); /* Verify that the path returned from uv_exepath is a subdirectory of executable_path */ - ASSERT(match && !strcmp(match, executable_path)); + ASSERT(match && !strcmp(match, path)); ASSERT(size == strlen(buffer)); /* Negative tests */ diff --git a/src/rt/libuv/test/test-getaddrinfo.c b/src/rt/libuv/test/test-getaddrinfo.c index a33e3d075cc..2a8c94e7552 100644 --- a/src/rt/libuv/test/test-getaddrinfo.c +++ b/src/rt/libuv/test/test-getaddrinfo.c @@ -31,10 +31,10 @@ static const char* name = "localhost"; -static uv_getaddrinfo_t getaddrinfo_handle; static int getaddrinfo_cbs = 0; /* data used for running multiple calls concurrently */ +static uv_getaddrinfo_t* getaddrinfo_handle; static uv_getaddrinfo_t getaddrinfo_handles[CONCURRENT_COUNT]; static int callback_counts[CONCURRENT_COUNT]; @@ -42,8 +42,10 @@ static int callback_counts[CONCURRENT_COUNT]; static void getaddrinfo_basic_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { - ASSERT(handle == &getaddrinfo_handle); + ASSERT(handle == getaddrinfo_handle); getaddrinfo_cbs++; + free(handle); + uv_freeaddrinfo(res); } @@ -51,32 +53,38 @@ static void getaddrinfo_cuncurrent_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { int i; + int* data = (int*)handle->data; for (i = 0; i < CONCURRENT_COUNT; i++) { if (&getaddrinfo_handles[i] == handle) { + ASSERT(i == *data); + callback_counts[i]++; break; } } ASSERT (i < CONCURRENT_COUNT); + free(data); + uv_freeaddrinfo(res); + getaddrinfo_cbs++; } TEST_IMPL(getaddrinfo_basic) { int r; + getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t)); - uv_init(); - - r = uv_getaddrinfo(&getaddrinfo_handle, + r = uv_getaddrinfo(uv_default_loop(), + getaddrinfo_handle, &getaddrinfo_basic_cb, name, NULL, NULL); ASSERT(r == 0); - uv_run(); + uv_run(uv_default_loop()); ASSERT(getaddrinfo_cbs == 1); @@ -86,21 +94,25 @@ TEST_IMPL(getaddrinfo_basic) { TEST_IMPL(getaddrinfo_concurrent) { int i, r; - - uv_init(); + int* data; for (i = 0; i < CONCURRENT_COUNT; i++) { callback_counts[i] = 0; - r = uv_getaddrinfo(&getaddrinfo_handles[i], - &getaddrinfo_cuncurrent_cb, - name, - NULL, - NULL); + data = (int*)malloc(sizeof(int)); + *data = i; + getaddrinfo_handles[i].data = data; + + r = uv_getaddrinfo(uv_default_loop(), + &getaddrinfo_handles[i], + &getaddrinfo_cuncurrent_cb, + name, + NULL, + NULL); ASSERT(r == 0); } - uv_run(); + uv_run(uv_default_loop()); for (i = 0; i < CONCURRENT_COUNT; i++) { ASSERT(callback_counts[i] == 1); diff --git a/src/rt/libuv/test/test-gethostbyname.c b/src/rt/libuv/test/test-gethostbyname.c index cbd25343bbb..583622e7245 100644 --- a/src/rt/libuv/test/test-gethostbyname.c +++ b/src/rt/libuv/test/test-gethostbyname.c @@ -74,8 +74,7 @@ static void prep_tcploopback() { options.tcp_port = htons(TEST_PORT); options.flags = ARES_FLAG_USEVC; - rc = uv_ares_init_options(&channel, &options, optmask); - + rc = uv_ares_init_options(uv_default_loop(), &channel, &options, optmask); ASSERT(rc == ARES_SUCCESS); } @@ -91,8 +90,6 @@ TEST_IMPL(gethostbyname) { return 1; } - uv_init(); - printf("Start basic gethostbyname test\n"); prep_tcploopback(); @@ -104,11 +101,11 @@ TEST_IMPL(gethostbyname) { AF_INET, &aresbynamecallback, &bynamecallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_bynamecallbacks == 1); - uv_ares_destroy(channel); + uv_ares_destroy(uv_default_loop(), channel); printf("Done basic gethostbyname test\n"); @@ -125,7 +122,7 @@ TEST_IMPL(gethostbyname) { AF_INET, &aresbynamecallback, &bynamecallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_bynamecallbacks == 1); @@ -143,11 +140,11 @@ TEST_IMPL(gethostbyname) { &aresbyaddrcallback, &byaddrcallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_byaddrcallbacks == 1); - uv_ares_destroy(channel); + uv_ares_destroy(uv_default_loop(), channel); printf("Done gethostbyname and gethostbyaddr sequential test\n"); @@ -179,13 +176,13 @@ TEST_IMPL(gethostbyname) { &aresbyaddrcallback, &byaddrcallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_bynamecallbacks == 1); ASSERT(ares_byaddrcallbacks == 1); - uv_ares_destroy(channel); + uv_ares_destroy(uv_default_loop(), channel); printf("Done gethostbyname and gethostbyaddr concurrent test\n"); return 0; diff --git a/src/rt/libuv/test/test-getsockname.c b/src/rt/libuv/test/test-getsockname.c new file mode 100644 index 00000000000..5dac88b73e4 --- /dev/null +++ b/src/rt/libuv/test/test-getsockname.c @@ -0,0 +1,342 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +static const int server_port = TEST_PORT; +/* Will be updated right after making the uv_connect_call */ +static int connect_port = -1; + +static int getsocknamecount = 0; +static int getpeernamecount = 0; + +static uv_loop_t* loop; +static uv_tcp_t tcp; +static uv_udp_t udp; +static uv_connect_t connect_req; +static uv_tcp_t tcpServer; +static uv_udp_t udpServer; +static uv_udp_send_t send_req; + + +static uv_buf_t alloc(uv_handle_t* handle, size_t suggested_size) { + uv_buf_t buf; + buf.base = (char*) malloc(suggested_size); + buf.len = suggested_size; + return buf; +} + + +static void on_close(uv_handle_t* peer) { + free(peer); + uv_close((uv_handle_t*)&tcpServer, NULL); +} + + +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*) req->handle, on_close); + free(req); +} + + +static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { + uv_shutdown_t* req; + int r; + + if (buf.base) { + free(buf.base); + } + + req = (uv_shutdown_t*) malloc(sizeof *req); + r = uv_shutdown(req, handle, after_shutdown); + ASSERT(r == 0); +} + + +static void check_sockname(struct sockaddr* addr, const char* compare_ip, + int compare_port, const char* context) { + struct sockaddr_in check_addr = *(struct sockaddr_in*) addr; + struct sockaddr_in compare_addr = uv_ip4_addr(compare_ip, compare_port); + char check_ip[17]; + int r; + + /* Both adresses should be ipv4 */ + ASSERT(check_addr.sin_family == AF_INET); + ASSERT(compare_addr.sin_family == AF_INET); + + /* Check if the ip matches */ + ASSERT(memcmp(&check_addr.sin_addr, + &compare_addr.sin_addr, + sizeof compare_addr.sin_addr) == 0); + + /* Check if the port matches. If port == 0 anything goes. */ + ASSERT(compare_port == 0 || check_addr.sin_port == compare_addr.sin_port); + + r = uv_ip4_name(&check_addr, (char*) check_ip, sizeof check_ip); + ASSERT(r == 0); + + printf("%s: %s:%d\n", context, check_ip, ntohs(check_addr.sin_port)); +} + + +static void on_connection(uv_stream_t* server, int status) { + struct sockaddr sockname, peername; + int namelen; + uv_handle_t* handle; + int r; + + if (status != 0) { + fprintf(stderr, "Connect error %d\n", + uv_last_error(loop).code); + } + ASSERT(status == 0); + + handle = (uv_handle_t*) malloc(sizeof(uv_tcp_t)); + ASSERT(handle != NULL); + + r = uv_tcp_init(loop, (uv_tcp_t*)handle); + ASSERT(r == 0); + + /* associate server with stream */ + handle->data = server; + + r = uv_accept(server, (uv_stream_t*)handle); + ASSERT(r == 0); + + namelen = sizeof sockname; + r = uv_tcp_getsockname((uv_tcp_t*) handle, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "127.0.0.1", server_port, "accepted socket"); + getsocknamecount++; + + namelen = sizeof peername; + r = uv_tcp_getpeername((uv_tcp_t*) handle, &peername, &namelen); + ASSERT(r == 0); + check_sockname(&peername, "127.0.0.1", connect_port, "accepted socket peer"); + getpeernamecount++; + + r = uv_read_start((uv_stream_t*)handle, alloc, after_read); + ASSERT(r == 0); +} + + +static void on_connect(uv_connect_t* req, int status) { + struct sockaddr sockname, peername; + int r, namelen; + + ASSERT(status == 0); + + namelen = sizeof sockname; + r = uv_tcp_getsockname((uv_tcp_t*) req->handle, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "127.0.0.1", 0, "connected socket"); + getsocknamecount++; + + namelen = sizeof peername; + r = uv_tcp_getpeername((uv_tcp_t*) req->handle, &peername, &namelen); + ASSERT(r == 0); + check_sockname(&peername, "127.0.0.1", server_port, "connected socket peer"); + getpeernamecount++; + + uv_close((uv_handle_t*)&tcp, NULL); +} + + +static int tcp_listener() { + struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", server_port); + struct sockaddr sockname, peername; + int namelen; + int r; + + r = uv_tcp_init(loop, &tcpServer); + if (r) { + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_tcp_bind(&tcpServer, addr); + if (r) { + fprintf(stderr, "Bind error\n"); + return 1; + } + + r = uv_listen((uv_stream_t*)&tcpServer, 128, on_connection); + if (r) { + fprintf(stderr, "Listen error\n"); + return 1; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof sockname; + r = uv_tcp_getsockname(&tcpServer, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", server_port, "server socket"); + getsocknamecount++; + + namelen = sizeof sockname; + r = uv_tcp_getpeername(&tcpServer, &peername, &namelen); + ASSERT(r == -1); + ASSERT(uv_last_error(loop).code == UV_ENOTCONN); + getpeernamecount++; + + return 0; +} + + +static void tcp_connector() { + struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", server_port); + struct sockaddr sockname; + int r, namelen; + + r = uv_tcp_init(loop, &tcp); + tcp.data = &connect_req; + ASSERT(!r); + + r = uv_tcp_connect(&connect_req, &tcp, server_addr, on_connect); + ASSERT(!r); + + /* Fetch the actual port used by the connecting socket. */ + namelen = sizeof sockname; + r = uv_tcp_getsockname(&tcp, &sockname, &namelen); + ASSERT(!r); + ASSERT(sockname.sa_family == AF_INET); + connect_port = ntohs(((struct sockaddr_in*) &sockname)->sin_port); + ASSERT(connect_port > 0); +} + + +static void udp_recv(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + struct sockaddr sockname; + int namelen; + int r; + + ASSERT(nread >= 0); + + if (nread == 0) { + free(buf.base); + return; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof(sockname); + r = uv_udp_getsockname(&udp, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", 0, "udp receiving socket"); + getsocknamecount++; + + uv_close((uv_handle_t*) &udp, NULL); + uv_close((uv_handle_t*) handle, NULL); +} + + +static void udp_send(uv_udp_send_t* req, int status) { + +} + + +static int udp_listener() { + struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", server_port); + struct sockaddr sockname; + int namelen; + int r; + + r = uv_udp_init(loop, &udpServer); + if (r) { + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_udp_bind(&udpServer, addr, 0); + if (r) { + fprintf(stderr, "Bind error\n"); + return 1; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof sockname; + r = uv_udp_getsockname(&udpServer, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", server_port, "udp listener socket"); + getsocknamecount++; + + r = uv_udp_recv_start(&udpServer, alloc, udp_recv); + ASSERT(r == 0); + + return 0; +} + + +static void udp_sender(void) { + struct sockaddr_in server_addr; + uv_buf_t buf; + int r; + + r = uv_udp_init(loop, &udp); + ASSERT(!r); + + buf = uv_buf_init("PING", 4); + server_addr = uv_ip4_addr("127.0.0.1", server_port); + + r = uv_udp_send(&send_req, &udp, &buf, 1, server_addr, udp_send); + ASSERT(!r); +} + + +TEST_IMPL(getsockname_tcp) { + loop = uv_default_loop(); + + if (tcp_listener()) + return 1; + + tcp_connector(); + + uv_run(loop); + + ASSERT(getsocknamecount == 3); + ASSERT(getpeernamecount == 3); + + return 0; +} + + +TEST_IMPL(getsockname_udp) { + loop = uv_default_loop(); + + if (udp_listener()) + return 1; + + udp_sender(); + + uv_run(loop); + + ASSERT(getsocknamecount == 2); + + return 0; +} diff --git a/src/rt/libuv/test/test-hrtime.c b/src/rt/libuv/test/test-hrtime.c index 4d96e3331ec..566e0d2ee02 100644 --- a/src/rt/libuv/test/test-hrtime.c +++ b/src/rt/libuv/test/test-hrtime.c @@ -22,9 +22,8 @@ #include "uv.h" #include "task.h" - -#ifndef MICROSEC -# define MICROSEC 1000000 +#ifndef MILLISEC +# define MILLISEC 1000 #endif #ifndef NANOSEC @@ -32,11 +31,6 @@ #endif - -/* - * We expect the amount of time passed to be at least one us plus two system - * calls. Therefore checking that at least a microsecond has elapsed is safe. - */ TEST_IMPL(hrtime) { uint64_t a, b, diff; @@ -46,9 +40,12 @@ TEST_IMPL(hrtime) { diff = b - a; - printf("diff = %llu\n", diff); + printf("diff = %llu\n", (unsigned long long int) diff); - ASSERT(diff >= NANOSEC / MICROSEC); - ASSERT(diff > MICROSEC); + /* The windows Sleep() function has only a resolution of 10-20 ms. */ + /* Check that the difference between the two hrtime values is somewhat in */ + /* the range we expect it to be. */ + ASSERT(diff > (uint64_t) 80 * NANOSEC / MILLISEC); + ASSERT(diff < (uint64_t) 120 * NANOSEC / MILLISEC); return 0; } diff --git a/src/rt/libuv/test/test-idle.c b/src/rt/libuv/test/test-idle.c new file mode 100644 index 00000000000..95ef3a94959 --- /dev/null +++ b/src/rt/libuv/test/test-idle.c @@ -0,0 +1,81 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + + +static uv_timer_t timer_handle; +static uv_idle_t idle_handle; + +static int idle_cb_called = 0; +static int timer_cb_called = 0; +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle, int status) { + ASSERT(handle == &timer_handle); + ASSERT(status == 0); + + uv_close((uv_handle_t*) &idle_handle, close_cb); + uv_close((uv_handle_t*) &timer_handle, close_cb); + + timer_cb_called++; + LOGF("timer_cb %d\n", timer_cb_called); +} + + +static void idle_cb(uv_idle_t* handle, int status) { + ASSERT(handle == &idle_handle); + ASSERT(status == 0); + + idle_cb_called++; + LOGF("idle_cb %d\n", idle_cb_called); +} + + +TEST_IMPL(idle_starvation) { + int r; + + r = uv_idle_init(uv_default_loop(), &idle_handle); + ASSERT(r == 0); + r = uv_idle_start(&idle_handle, idle_cb); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer_handle); + ASSERT(r == 0); + r = uv_timer_start(&timer_handle, timer_cb, 50, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(idle_cb_called > 0); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 2); + + return 0; +} diff --git a/src/rt/libuv/test/test-list.h b/src/rt/libuv/test/test-list.h index 190574acaa8..bf376e39141 100644 --- a/src/rt/libuv/test/test-list.h +++ b/src/rt/libuv/test/test-list.h @@ -19,87 +19,152 @@ * IN THE SOFTWARE. */ -TEST_DECLARE (ping_pong) -TEST_DECLARE (ping_pong_v6) +TEST_DECLARE (tty) +TEST_DECLARE (tcp_ping_pong) +TEST_DECLARE (tcp_ping_pong_v6) +TEST_DECLARE (pipe_ping_pong) TEST_DECLARE (delayed_accept) TEST_DECLARE (tcp_writealot) -TEST_DECLARE (bind_error_addrinuse) -TEST_DECLARE (bind_error_addrnotavail_1) -TEST_DECLARE (bind_error_addrnotavail_2) -TEST_DECLARE (bind_error_fault) -TEST_DECLARE (bind_error_inval) -TEST_DECLARE (bind_localhost_ok) -TEST_DECLARE (bind6_error_addrinuse) -TEST_DECLARE (bind6_error_addrnotavail) -TEST_DECLARE (bind6_error_fault) -TEST_DECLARE (bind6_error_inval) -TEST_DECLARE (bind6_localhost_ok) +TEST_DECLARE (tcp_bind_error_addrinuse) +TEST_DECLARE (tcp_bind_error_addrnotavail_1) +TEST_DECLARE (tcp_bind_error_addrnotavail_2) +TEST_DECLARE (tcp_bind_error_fault) +TEST_DECLARE (tcp_bind_error_inval) +TEST_DECLARE (tcp_bind_localhost_ok) +TEST_DECLARE (tcp_listen_without_bind) +TEST_DECLARE (tcp_close) +TEST_DECLARE (tcp_write_error) +TEST_DECLARE (tcp_bind6_error_addrinuse) +TEST_DECLARE (tcp_bind6_error_addrnotavail) +TEST_DECLARE (tcp_bind6_error_fault) +TEST_DECLARE (tcp_bind6_error_inval) +TEST_DECLARE (tcp_bind6_localhost_ok) +TEST_DECLARE (udp_send_and_recv) +TEST_DECLARE (udp_dgram_too_big) +TEST_DECLARE (udp_dual_stack) +TEST_DECLARE (udp_ipv6_only) +TEST_DECLARE (pipe_bind_error_addrinuse) +TEST_DECLARE (pipe_bind_error_addrnotavail) +TEST_DECLARE (pipe_bind_error_inval) +TEST_DECLARE (pipe_listen_without_bind) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) TEST_DECLARE (shutdown_eof) TEST_DECLARE (callback_stack) TEST_DECLARE (timer) TEST_DECLARE (timer_again) +TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) TEST_DECLARE (ref) TEST_DECLARE (idle_ref) TEST_DECLARE (async_ref) TEST_DECLARE (prepare_ref) TEST_DECLARE (check_ref) +TEST_DECLARE (unref_in_prepare_cb) TEST_DECLARE (async) TEST_DECLARE (get_currentexe) TEST_DECLARE (hrtime) TEST_DECLARE (getaddrinfo_basic) TEST_DECLARE (getaddrinfo_concurrent) TEST_DECLARE (gethostbyname) +TEST_DECLARE (getsockname_tcp) +TEST_DECLARE (getsockname_udp) TEST_DECLARE (fail_always) TEST_DECLARE (pass_always) -HELPER_DECLARE (echo_server) +TEST_DECLARE (spawn_exit_code) +TEST_DECLARE (spawn_stdout) +TEST_DECLARE (spawn_stdin) +TEST_DECLARE (spawn_and_kill) +TEST_DECLARE (fs_file_noent) +TEST_DECLARE (fs_file_async) +TEST_DECLARE (fs_file_sync) +TEST_DECLARE (fs_async_dir) +TEST_DECLARE (fs_async_sendfile) +TEST_DECLARE (fs_fstat) +TEST_DECLARE (fs_chmod) +TEST_DECLARE (fs_chown) +TEST_DECLARE (fs_link) +TEST_DECLARE (fs_symlink) +TEST_DECLARE (fs_utime) +TEST_DECLARE (fs_futime) +TEST_DECLARE (fs_event_watch_dir) +TEST_DECLARE (fs_event_watch_file) +TEST_DECLARE (fs_event_watch_file_current_dir) +TEST_DECLARE (threadpool_queue_work_simple) +#ifdef _WIN32 +TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) +TEST_DECLARE (argument_escaping) +TEST_DECLARE (environment_creation) +#endif +HELPER_DECLARE (tcp4_echo_server) +HELPER_DECLARE (tcp6_echo_server) +HELPER_DECLARE (pipe_echo_server) TASK_LIST_START - TEST_ENTRY (ping_pong) - TEST_HELPER (ping_pong, echo_server) + TEST_ENTRY (tty) - TEST_ENTRY (ping_pong_v6) - TEST_HELPER (ping_pong_v6, echo_server) + TEST_ENTRY (tcp_ping_pong) + TEST_HELPER (tcp_ping_pong, tcp4_echo_server) + + TEST_ENTRY (tcp_ping_pong_v6) + TEST_HELPER (tcp_ping_pong_v6, tcp6_echo_server) + + TEST_ENTRY (pipe_ping_pong) + TEST_HELPER (pipe_ping_pong, pipe_echo_server) TEST_ENTRY (delayed_accept) TEST_ENTRY (tcp_writealot) - TEST_HELPER (tcp_writealot, echo_server) + TEST_HELPER (tcp_writealot, tcp4_echo_server) - TEST_ENTRY (bind_error_addrinuse) - TEST_ENTRY (bind_error_addrnotavail_1) - TEST_ENTRY (bind_error_addrnotavail_2) - TEST_ENTRY (bind_error_fault) - TEST_ENTRY (bind_error_inval) - TEST_ENTRY (bind_localhost_ok) + TEST_ENTRY (tcp_bind_error_addrinuse) + TEST_ENTRY (tcp_bind_error_addrnotavail_1) + TEST_ENTRY (tcp_bind_error_addrnotavail_2) + TEST_ENTRY (tcp_bind_error_fault) + TEST_ENTRY (tcp_bind_error_inval) + TEST_ENTRY (tcp_bind_localhost_ok) + TEST_ENTRY (tcp_listen_without_bind) + TEST_ENTRY (tcp_close) + TEST_ENTRY (tcp_write_error) - TEST_ENTRY (bind6_error_addrinuse) - TEST_ENTRY (bind6_error_addrnotavail) - TEST_ENTRY (bind6_error_fault) - TEST_ENTRY (bind6_error_inval) - TEST_ENTRY (bind6_localhost_ok) + TEST_ENTRY (tcp_bind6_error_addrinuse) + TEST_ENTRY (tcp_bind6_error_addrnotavail) + TEST_ENTRY (tcp_bind6_error_fault) + TEST_ENTRY (tcp_bind6_error_inval) + TEST_ENTRY (tcp_bind6_localhost_ok) + + TEST_ENTRY (udp_send_and_recv) + TEST_ENTRY (udp_dgram_too_big) + TEST_ENTRY (udp_dual_stack) + TEST_ENTRY (udp_ipv6_only) + + TEST_ENTRY (pipe_bind_error_addrinuse) + TEST_ENTRY (pipe_bind_error_addrnotavail) + TEST_ENTRY (pipe_bind_error_inval) + TEST_ENTRY (pipe_listen_without_bind) TEST_ENTRY (connection_fail) TEST_ENTRY (connection_fail_doesnt_auto_close) TEST_ENTRY (shutdown_eof) - TEST_HELPER (shutdown_eof, echo_server) + TEST_HELPER (shutdown_eof, tcp4_echo_server) TEST_ENTRY (callback_stack) - TEST_HELPER (callback_stack, echo_server) + TEST_HELPER (callback_stack, tcp4_echo_server) TEST_ENTRY (timer) TEST_ENTRY (timer_again) + TEST_ENTRY (idle_starvation) + TEST_ENTRY (ref) TEST_ENTRY (idle_ref) TEST_ENTRY (async_ref) TEST_ENTRY (prepare_ref) TEST_ENTRY (check_ref) + TEST_ENTRY (unref_in_prepare_cb) TEST_ENTRY (loop_handles) @@ -113,7 +178,37 @@ TASK_LIST_START TEST_ENTRY (getaddrinfo_concurrent) TEST_ENTRY (gethostbyname) - TEST_HELPER (gethostbyname, echo_server) + TEST_HELPER (gethostbyname, tcp4_echo_server) + + TEST_ENTRY (getsockname_tcp) + TEST_ENTRY (getsockname_udp) + + TEST_ENTRY (spawn_exit_code) + TEST_ENTRY (spawn_stdout) + TEST_ENTRY (spawn_stdin) + TEST_ENTRY (spawn_and_kill) +#ifdef _WIN32 + TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) + TEST_ENTRY (argument_escaping) + TEST_ENTRY (environment_creation) +#endif + + TEST_ENTRY (fs_file_noent) + TEST_ENTRY (fs_file_async) + TEST_ENTRY (fs_file_sync) + TEST_ENTRY (fs_async_dir) + TEST_ENTRY (fs_async_sendfile) + TEST_ENTRY (fs_fstat) + TEST_ENTRY (fs_chmod) + TEST_ENTRY (fs_chown) + TEST_ENTRY (fs_utime) + TEST_ENTRY (fs_futime) + TEST_ENTRY (fs_symlink) + TEST_ENTRY (fs_event_watch_dir) + TEST_ENTRY (fs_event_watch_file) + TEST_ENTRY (fs_event_watch_file_current_dir) + + TEST_ENTRY (threadpool_queue_work_simple) #if 0 /* These are for testing the test runner. */ @@ -121,4 +216,3 @@ TASK_LIST_START TEST_ENTRY (pass_always) #endif TASK_LIST_END - diff --git a/src/rt/libuv/test/test-loop-handles.c b/src/rt/libuv/test/test-loop-handles.c index 7979a74b4ae..9a76cc53fa6 100644 --- a/src/rt/libuv/test/test-loop-handles.c +++ b/src/rt/libuv/test/test-loop-handles.c @@ -130,8 +130,6 @@ static void idle_2_close_cb(uv_handle_t* handle) { static void idle_2_cb(uv_idle_t* handle, int status) { - int r; - LOG("IDLE_2_CB\n"); ASSERT(handle == &idle_2_handle); @@ -139,8 +137,7 @@ static void idle_2_cb(uv_idle_t* handle, int status) { idle_2_cb_called++; - r = uv_close((uv_handle_t*)handle, idle_2_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)handle, idle_2_close_cb); } @@ -156,7 +153,7 @@ static void idle_1_cb(uv_idle_t* handle, int status) { /* Init idle_2 and make it active */ if (!idle_2_is_active) { - r = uv_idle_init(&idle_2_handle); + r = uv_idle_init(uv_default_loop(), &idle_2_handle); ASSERT(r == 0); r = uv_idle_start(&idle_2_handle, idle_2_cb); ASSERT(r == 0); @@ -230,23 +227,18 @@ static void check_cb(uv_check_t* handle, int status) { } else { /* End of the test - close all handles */ - r = uv_close((uv_handle_t*)&prepare_1_handle, prepare_1_close_cb); - ASSERT(r == 0); - r = uv_close((uv_handle_t*)&check_handle, check_close_cb); - ASSERT(r == 0); - r = uv_close((uv_handle_t*)&prepare_2_handle, prepare_2_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)&prepare_1_handle, prepare_1_close_cb); + uv_close((uv_handle_t*)&check_handle, check_close_cb); + uv_close((uv_handle_t*)&prepare_2_handle, prepare_2_close_cb); for (i = 0; i < IDLE_COUNT; i++) { - r = uv_close((uv_handle_t*)&idle_1_handles[i], idle_1_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)&idle_1_handles[i], idle_1_close_cb); } /* This handle is closed/recreated every time, close it only if it is */ /* active.*/ if (idle_2_is_active) { - r = uv_close((uv_handle_t*)&idle_2_handle, idle_2_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)&idle_2_handle, idle_2_close_cb); } } @@ -307,25 +299,23 @@ TEST_IMPL(loop_handles) { int i; int r; - uv_init(); - - r = uv_prepare_init(&prepare_1_handle); + r = uv_prepare_init(uv_default_loop(), &prepare_1_handle); ASSERT(r == 0); r = uv_prepare_start(&prepare_1_handle, prepare_1_cb); ASSERT(r == 0); - r = uv_check_init(&check_handle); + r = uv_check_init(uv_default_loop(), &check_handle); ASSERT(r == 0); r = uv_check_start(&check_handle, check_cb); ASSERT(r == 0); /* initialize only, prepare_2 is started by prepare_1_cb */ - r = uv_prepare_init(&prepare_2_handle); + r = uv_prepare_init(uv_default_loop(), &prepare_2_handle); ASSERT(r == 0); for (i = 0; i < IDLE_COUNT; i++) { /* initialize only, idle_1 handles are started by check_cb */ - r = uv_idle_init(&idle_1_handles[i]); + r = uv_idle_init(uv_default_loop(), &idle_1_handles[i]); ASSERT(r == 0); } @@ -333,13 +323,13 @@ TEST_IMPL(loop_handles) { /* the timer callback is there to keep the event loop polling */ /* unref it as it is not supposed to keep the loop alive */ - r = uv_timer_init(&timer_handle); + r = uv_timer_init(uv_default_loop(), &timer_handle); ASSERT(r == 0); r = uv_timer_start(&timer_handle, timer_cb, TIMEOUT, TIMEOUT); ASSERT(r == 0); - uv_unref(); + uv_unref(uv_default_loop()); - r = uv_run(); + r = uv_run(uv_default_loop()); ASSERT(r == 0); ASSERT(loop_iteration == ITERATIONS); @@ -367,53 +357,3 @@ TEST_IMPL(loop_handles) { return 0; } - - -TEST_IMPL(ref) { - uv_init(); - uv_run(); - return 0; -} - - -TEST_IMPL(idle_ref) { - uv_idle_t h; - uv_init(); - uv_idle_init(&h); - uv_idle_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(async_ref) { - uv_async_t h; - uv_init(); - uv_async_init(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(prepare_ref) { - uv_prepare_t h; - uv_init(); - uv_prepare_init(&h); - uv_prepare_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(check_ref) { - uv_check_t h; - uv_init(); - uv_check_init(&h); - uv_check_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} diff --git a/src/rt/libuv/test/test-ping-pong.c b/src/rt/libuv/test/test-ping-pong.c index 34de78b5b6c..f452fce50b5 100644 --- a/src/rt/libuv/test/test-ping-pong.c +++ b/src/rt/libuv/test/test-ping-pong.c @@ -34,21 +34,24 @@ static int completed_pingers = 0; #define BUFSIZE 10240 static char PING[] = "PING\n"; +static int pinger_on_connect_count; typedef struct { int pongs; int state; - uv_tcp_t tcp; - uv_req_t connect_req; - uv_req_t read_req; + union { + uv_tcp_t tcp; + uv_pipe_t pipe; + } stream; + uv_connect_t connect_req; char read_buffer[BUFSIZE]; } pinger_t; void pinger_try_read(pinger_t* pinger); -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -67,25 +70,22 @@ static void pinger_on_close(uv_handle_t* handle) { } -static void pinger_after_write(uv_req_t *req, int status) { +static void pinger_after_write(uv_write_t *req, int status) { ASSERT(status == 0); - free(req); } static void pinger_write_ping(pinger_t* pinger) { - uv_req_t *req; + uv_write_t *req; uv_buf_t buf; buf.base = (char*)&PING; buf.len = strlen(PING); - req = (uv_req_t*)malloc(sizeof(*req)); - uv_req_init(req, (uv_handle_t*)(&pinger->tcp), - (void *(*)(void *))pinger_after_write); + req = malloc(sizeof(uv_write_t)); - if (uv_write(req, &buf, 1)) { + if (uv_write(req, (uv_stream_t*)&pinger->stream.tcp, &buf, 1, pinger_after_write)) { FATAL("uv_write failed"); } @@ -93,14 +93,14 @@ static void pinger_write_ping(pinger_t* pinger) { } -static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { - unsigned int i; +static void pinger_read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { + ssize_t i; pinger_t* pinger; - pinger = (pinger_t*)tcp->data; + pinger = (pinger_t*)stream->data; if (nread < 0) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); puts("got EOF"); @@ -108,7 +108,7 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { free(buf.base); } - uv_close((uv_handle_t*)(&pinger->tcp), pinger_on_close); + uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); return; } @@ -123,7 +123,7 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { if (pinger->pongs < NUM_PINGS) { pinger_write_ping(pinger); } else { - uv_close((uv_handle_t*)(&pinger->tcp), pinger_on_close); + uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); return; } } @@ -131,9 +131,11 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { } -static void pinger_on_connect(uv_req_t *req, int status) { +static void pinger_on_connect(uv_connect_t *req, int status) { pinger_t *pinger = (pinger_t*)req->handle->data; + pinger_on_connect_count++; + ASSERT(status == 0); pinger_write_ping(pinger); @@ -142,44 +144,8 @@ static void pinger_on_connect(uv_req_t *req, int status) { } -static void pinger_new() { - int r; - struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); - pinger_t *pinger; - - pinger = (pinger_t*)malloc(sizeof(*pinger)); - pinger->state = 0; - pinger->pongs = 0; - - /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&pinger->tcp); - pinger->tcp.data = pinger; - ASSERT(!r); - - /* We are never doing multiple reads/connects at a time anyway. */ - /* so these handles can be pre-initialized. */ - uv_req_init(&pinger->connect_req, (uv_handle_t*)(&pinger->tcp), - (void *(*)(void *))pinger_on_connect); - - r = uv_tcp_connect(&pinger->connect_req, server_addr); - ASSERT(!r); -} - - -TEST_IMPL(ping_pong) { - uv_init(); - - pinger_new(); - uv_run(); - - ASSERT(completed_pingers == 1); - - return 0; -} - - /* same ping-pong test, but using IPv6 connection */ -static void pinger_v6_new() { +static void tcp_pinger_v6_new() { int r; struct sockaddr_in6 server_addr = uv_ip6_addr("::1", TEST_PORT); pinger_t *pinger; @@ -189,25 +155,94 @@ static void pinger_v6_new() { pinger->pongs = 0; /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&pinger->tcp); - pinger->tcp.data = pinger; + r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); + pinger->stream.tcp.data = pinger; ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ - uv_req_init(&pinger->connect_req, (uv_handle_t*)(&pinger->tcp), - (void *(*)(void *))pinger_on_connect); - - r = uv_tcp_connect6(&pinger->connect_req, server_addr); + r = uv_tcp_connect6(&pinger->connect_req, &pinger->stream.tcp, server_addr, + pinger_on_connect); ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); } -TEST_IMPL(ping_pong_v6) { - uv_init(); +static void tcp_pinger_new() { + int r; + struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + pinger_t *pinger; - pinger_v6_new(); - uv_run(); + pinger = (pinger_t*)malloc(sizeof(*pinger)); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connec to the server and do NUM_PINGS ping-pongs. */ + r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); + pinger->stream.tcp.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + r = uv_tcp_connect(&pinger->connect_req, &pinger->stream.tcp, server_addr, + pinger_on_connect); + ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); +} + + +static void pipe_pinger_new() { + int r; + pinger_t *pinger; + + pinger = (pinger_t*)malloc(sizeof(*pinger)); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connec to the server and do NUM_PINGS ping-pongs. */ + r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe); + pinger->stream.pipe.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + + r = uv_pipe_connect(&pinger->connect_req, &pinger->stream.pipe, TEST_PIPENAME, + pinger_on_connect); + ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); +} + + +TEST_IMPL(tcp_ping_pong) { + tcp_pinger_new(); + uv_run(uv_default_loop()); + + ASSERT(completed_pingers == 1); + + return 0; +} + + +TEST_IMPL(tcp_ping_pong_v6) { + tcp_pinger_v6_new(); + uv_run(uv_default_loop()); + + ASSERT(completed_pingers == 1); + + return 0; +} + + +TEST_IMPL(pipe_ping_pong) { + pipe_pinger_new(); + uv_run(uv_default_loop()); ASSERT(completed_pingers == 1); diff --git a/src/rt/libuv/test/test-pipe-bind-error.c b/src/rt/libuv/test/test-pipe-bind-error.c new file mode 100644 index 00000000000..832ce023153 --- /dev/null +++ b/src/rt/libuv/test/test-pipe-bind-error.c @@ -0,0 +1,140 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include +#include + + +#ifdef _WIN32 +# define BAD_PIPENAME "bad-pipe" +#else +# define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there" +#endif + + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(pipe_bind_error_addrinuse) { + uv_pipe_t server1, server2; + int r; + + r = uv_pipe_init(uv_default_loop(), &server1); + ASSERT(r == 0); + r = uv_pipe_bind(&server1, TEST_PIPENAME); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &server2); + ASSERT(r == 0); + r = uv_pipe_bind(&server2, TEST_PIPENAME); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRINUSE); + + r = uv_listen((uv_stream_t*)&server1, SOMAXCONN, NULL); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + + uv_close((uv_handle_t*)&server1, close_cb); + uv_close((uv_handle_t*)&server2, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 2); + + return 0; +} + + +TEST_IMPL(pipe_bind_error_addrnotavail) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_pipe_bind(&server, BAD_PIPENAME); + + ASSERT(r == -1); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EACCESS); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(pipe_bind_error_inval) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_pipe_bind(&server, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_pipe_bind(&server, TEST_PIPENAME_2); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(pipe_listen_without_bind) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-ref.c b/src/rt/libuv/test/test-ref.c new file mode 100644 index 00000000000..0083335fd12 --- /dev/null +++ b/src/rt/libuv/test/test-ref.c @@ -0,0 +1,85 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + + +TEST_IMPL(ref) { + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(idle_ref) { + uv_idle_t h; + uv_idle_init(uv_default_loop(), &h); + uv_idle_start(&h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(async_ref) { + uv_async_t h; + uv_async_init(uv_default_loop(), &h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(prepare_ref) { + uv_prepare_t h; + uv_prepare_init(uv_default_loop(), &h); + uv_prepare_start(&h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(check_ref) { + uv_check_t h; + uv_check_init(uv_default_loop(), &h); + uv_check_start(&h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +static void prepare_cb(uv_prepare_t* handle, int status) { + ASSERT(handle != NULL); + ASSERT(status == 0); + + uv_unref(uv_default_loop()); +} + + +TEST_IMPL(unref_in_prepare_cb) { + uv_prepare_t h; + uv_prepare_init(uv_default_loop(), &h); + uv_prepare_start(&h, prepare_cb); + uv_run(uv_default_loop()); + return 0; +} diff --git a/src/rt/libuv/test/test-shutdown-eof.c b/src/rt/libuv/test/test-shutdown-eof.c index 8a960c9ea6a..9d4f2cce74d 100644 --- a/src/rt/libuv/test/test-shutdown-eof.c +++ b/src/rt/libuv/test/test-shutdown-eof.c @@ -26,7 +26,9 @@ static uv_timer_t timer; static uv_tcp_t tcp; -static uv_req_t connect_req, write_req, shutdown_req; +static uv_connect_t connect_req; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; static uv_buf_t qbuf; static int got_q; static int got_eof; @@ -37,7 +39,7 @@ static int called_timer_close_cb; static int called_timer_cb; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -46,7 +48,7 @@ static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { static void read_cb(uv_stream_t* t, ssize_t nread, uv_buf_t buf) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(uv_default_loop()); ASSERT((uv_tcp_t*)t == &tcp); @@ -74,7 +76,7 @@ static void read_cb(uv_stream_t* t, ssize_t nread, uv_buf_t buf) { } -static void shutdown_cb(uv_req_t *req, int status) { +static void shutdown_cb(uv_shutdown_t *req, int status) { ASSERT(req == &shutdown_req); ASSERT(called_connect_cb == 1); @@ -87,7 +89,7 @@ static void shutdown_cb(uv_req_t *req, int status) { } -static void connect_cb(uv_req_t *req, int status) { +static void connect_cb(uv_connect_t *req, int status) { ASSERT(status == 0); ASSERT(req == &connect_req); @@ -98,12 +100,10 @@ static void connect_cb(uv_req_t *req, int status) { * Write the letter 'Q' to gracefully kill the echo-server. This will not * effect our connection. */ - uv_req_init(&write_req, (uv_handle_t*)&tcp, NULL); - uv_write(&write_req, &qbuf, 1); + uv_write(&write_req, (uv_stream_t*) &tcp, &qbuf, 1, NULL); /* Shutdown our end of the connection. */ - uv_req_init(&shutdown_req, (uv_handle_t*)&tcp, (void *(*)(void *))shutdown_cb); - uv_shutdown(&shutdown_req); + uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb); called_connect_cb++; ASSERT(called_shutdown_cb == 0); @@ -153,23 +153,22 @@ TEST_IMPL(shutdown_eof) { struct sockaddr_in server_addr; int r; - uv_init(); - qbuf.base = "Q"; qbuf.len = 1; - uv_timer_init(&timer); + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + uv_timer_start(&timer, timer_cb, 100, 0); server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); - r = uv_tcp_init(&tcp); + r = uv_tcp_init(uv_default_loop(), &tcp); ASSERT(!r); - uv_req_init(&connect_req, (uv_handle_t*) &tcp, (void *(*)(void *))connect_cb); - r = uv_tcp_connect(&connect_req, server_addr); + r = uv_tcp_connect(&connect_req, &tcp, server_addr, connect_cb); ASSERT(!r); - uv_run(); + uv_run(uv_default_loop()); ASSERT(called_connect_cb == 1); ASSERT(called_shutdown_cb == 1); diff --git a/src/rt/libuv/test/test-spawn.c b/src/rt/libuv/test/test-spawn.c new file mode 100644 index 00000000000..653f9ac95d3 --- /dev/null +++ b/src/rt/libuv/test/test-spawn.c @@ -0,0 +1,383 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include +#include +#include + +static int close_cb_called; +static int exit_cb_called; +static uv_process_t process; +static uv_timer_t timer; +static uv_process_options_t options; +static char exepath[1024]; +static size_t exepath_size = 1024; +static char* args[3]; + +#define OUTPUT_SIZE 1024 +static char output[OUTPUT_SIZE]; +static int output_used; + + +static void close_cb(uv_handle_t* handle) { + printf("close_cb\n"); + close_cb_called++; +} + + +static void exit_cb(uv_process_t* process, int exit_status, int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; + ASSERT(exit_status == 1); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, close_cb); +} + + +static void kill_cb(uv_process_t* process, int exit_status, int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; +#ifdef _WIN32 + ASSERT(exit_status == 1); +#else + ASSERT(exit_status == 0); +#endif + ASSERT(term_signal == 15); + uv_close((uv_handle_t*)process, close_cb); +} + + +uv_buf_t on_alloc(uv_handle_t* handle, size_t suggested_size) { + uv_buf_t buf; + buf.base = output + output_used; + buf.len = OUTPUT_SIZE - output_used; + return buf; +} + + +void on_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { + uv_err_t err = uv_last_error(uv_default_loop()); + + if (nread > 0) { + output_used += nread; + } else if (nread < 0) { + if (err.code == UV_EOF) { + uv_close((uv_handle_t*)tcp, close_cb); + } + } +} + + +void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + uv_close((uv_handle_t*)req->handle, close_cb); +} + + +static void init_process_options(char* test, uv_exit_cb exit_cb) { + /* Note spawn_helper1 defined in test/run-tests.c */ + int r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + args[0] = exepath; + args[1] = test; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; +} + + +static void timer_cb(uv_timer_t* handle, int status) { + uv_process_kill(&process, /* SIGTERM */ 15); + uv_close((uv_handle_t*)handle, close_cb); +} + + +TEST_IMPL(spawn_exit_code) { + int r; + + init_process_options("spawn_helper1", exit_cb); + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(spawn_stdout) { + int r; + uv_pipe_t out; + + init_process_options("spawn_helper2", exit_cb); + + uv_pipe_init(uv_default_loop(), &out); + options.stdout_stream = &out; + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0 || strcmp("hello world\r\n", output) == 0); + + return 0; +} + + +TEST_IMPL(spawn_stdin) { +int r; + uv_pipe_t out; + uv_pipe_t in; + uv_write_t write_req; + uv_buf_t buf; + char buffer[] = "hello-from-spawn_stdin"; + + init_process_options("spawn_helper3", exit_cb); + + uv_pipe_init(uv_default_loop(), &out); + uv_pipe_init(uv_default_loop(), &in); + options.stdout_stream = &out; + options.stdin_stream = ∈ + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + buf.base = buffer; + buf.len = sizeof(buffer); + r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 3); /* Once for process twice for the pipe. */ + ASSERT(strcmp(buffer, output) == 0); + + return 0; +} + + +TEST_IMPL(spawn_and_kill) { + int r; + + init_process_options("spawn_helper4", kill_cb); + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 500, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process and once for timer. */ + + return 0; +} + + +#ifdef _WIN32 +TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { + int r; + uv_pipe_t out; + char name[64]; + HANDLE pipe_handle; + + init_process_options("spawn_helper2", exit_cb); + + uv_pipe_init(uv_default_loop(), &out); + options.stdout_stream = &out; + + /* Create a pipe that'll cause a collision. */ + _snprintf(name, sizeof(name), "\\\\.\\pipe\\uv\\%p-%d", &out, GetCurrentProcessId()); + pipe_handle = CreateNamedPipeA(name, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 10, + 65536, + 65536, + 0, + NULL); + ASSERT(pipe_handle != INVALID_HANDLE_VALUE); + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0 || strcmp("hello world\r\n", output) == 0); + + return 0; +} + + +wchar_t* make_program_args(char** args, int verbatim_arguments); +wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target); + +TEST_IMPL(argument_escaping) { + const wchar_t* test_str[] = { + L"HelloWorld", + L"Hello World", + L"Hello\"World", + L"Hello World\\", + L"Hello\\\"World", + L"Hello\\World", + L"Hello\\\\World", + L"Hello World\\", + L"c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"" + }; + const int count = sizeof(test_str) / sizeof(*test_str); + wchar_t** test_output; + wchar_t* command_line; + wchar_t** cracked; + size_t total_size = 0; + int i; + int num_args; + + char* verbatim[] = { + "cmd.exe", + "/c", + "c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"", + NULL + }; + wchar_t* verbatim_output; + wchar_t* non_verbatim_output; + + test_output = calloc(count, sizeof(wchar_t*)); + for (i = 0; i < count; ++i) { + test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(wchar_t)); + quote_cmd_arg(test_str[i], test_output[i]); + wprintf(L"input : %s\n", test_str[i]); + wprintf(L"output: %s\n", test_output[i]); + total_size += wcslen(test_output[i]) + 1; + } + command_line = calloc(total_size + 1, sizeof(wchar_t)); + for (i = 0; i < count; ++i) { + wcscat(command_line, test_output[i]); + wcscat(command_line, L" "); + } + command_line[total_size - 1] = L'\0'; + + wprintf(L"command_line: %s\n", command_line); + + cracked = CommandLineToArgvW(command_line, &num_args); + for (i = 0; i < num_args; ++i) { + wprintf(L"%d: %s\t%s\n", i, test_str[i], cracked[i]); + ASSERT(wcscmp(test_str[i], cracked[i]) == 0); + } + + LocalFree(cracked); + for (i = 0; i < count; ++i) { + free(test_output[i]); + } + + verbatim_output = make_program_args(verbatim, 1); + non_verbatim_output = make_program_args(verbatim, 0); + + wprintf(L" verbatim_output: %s\n", verbatim_output); + wprintf(L"non_verbatim_output: %s\n", non_verbatim_output); + + ASSERT(wcscmp(verbatim_output, L"cmd.exe /c c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"") == 0); + ASSERT(wcscmp(non_verbatim_output, L"cmd.exe /c \"c:\\path\\to\\node.exe --eval \\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0); + + free(verbatim_output); + free(non_verbatim_output); + + return 0; +} + +wchar_t* make_program_env(char** env_block); + +TEST_IMPL(environment_creation) { + int i; + char* environment[] = { + "FOO=BAR", + "SYSTEM=ROOT", /* substring of a supplied var name */ + "SYSTEMROOTED=OMG", /* supplied var name is a substring */ + "TEMP=C:\\Temp", + "BAZ=QUX", + NULL + }; + + wchar_t expected[512]; + wchar_t* ptr = expected; + wchar_t* result; + wchar_t* str; + + for (i = 0; i < sizeof(environment) / sizeof(environment[0]) - 1; i++) { + ptr += uv_utf8_to_utf16(environment[i], ptr, expected + sizeof(expected) - ptr); + } + + memcpy(ptr, L"SYSTEMROOT=", sizeof(L"SYSTEMROOT=")); + ptr += sizeof(L"SYSTEMROOT=")/sizeof(wchar_t) - 1; + ptr += GetEnvironmentVariableW(L"SYSTEMROOT", ptr, expected + sizeof(expected) - ptr); + ++ptr; + + memcpy(ptr, L"SYSTEMDRIVE=", sizeof(L"SYSTEMDRIVE=")); + ptr += sizeof(L"SYSTEMDRIVE=")/sizeof(wchar_t) - 1; + ptr += GetEnvironmentVariableW(L"SYSTEMDRIVE", ptr, expected + sizeof(expected) - ptr); + ++ptr; + *ptr = '\0'; + + result = make_program_env(environment); + + for (str = result; *str; str += wcslen(str) + 1) { + wprintf(L"%s\n", str); + } + + ASSERT(wcscmp(expected, result) == 0); + + return 0; +} +#endif diff --git a/src/rt/libuv/test/test-bind-error.c b/src/rt/libuv/test/test-tcp-bind-error.c similarity index 71% rename from src/rt/libuv/test/test-bind-error.c rename to src/rt/libuv/test/test-tcp-bind-error.c index 4ac60654f6d..9512519ac0c 100644 --- a/src/rt/libuv/test/test-bind-error.c +++ b/src/rt/libuv/test/test-tcp-bind-error.c @@ -34,34 +34,32 @@ static void close_cb(uv_handle_t* handle) { } -TEST_IMPL(bind_error_addrinuse) { +TEST_IMPL(tcp_bind_error_addrinuse) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", TEST_PORT); uv_tcp_t server1, server2; int r; - uv_init(); - - r = uv_tcp_init(&server1); + r = uv_tcp_init(uv_default_loop(), &server1); ASSERT(r == 0); r = uv_tcp_bind(&server1, addr); ASSERT(r == 0); - r = uv_tcp_init(&server2); + r = uv_tcp_init(uv_default_loop(), &server2); ASSERT(r == 0); r = uv_tcp_bind(&server2, addr); ASSERT(r == 0); - r = uv_tcp_listen(&server1, 128, NULL); + r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); - r = uv_tcp_listen(&server2, 128, NULL); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRINUSE); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 2); @@ -69,25 +67,23 @@ TEST_IMPL(bind_error_addrinuse) { } -TEST_IMPL(bind_error_addrnotavail_1) { +TEST_IMPL(tcp_bind_error_addrnotavail_1) { struct sockaddr_in addr = uv_ip4_addr("127.255.255.255", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr); /* It seems that Linux is broken here - bind succeeds. */ if (r == -1) { - ASSERT(uv_last_error().code == UV_EADDRNOTAVAIL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRNOTAVAIL); } uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -95,22 +91,20 @@ TEST_IMPL(bind_error_addrnotavail_1) { } -TEST_IMPL(bind_error_addrnotavail_2) { +TEST_IMPL(tcp_bind_error_addrnotavail_2) { struct sockaddr_in addr = uv_ip4_addr("4.4.4.4", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRNOTAVAIL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -118,7 +112,7 @@ TEST_IMPL(bind_error_addrnotavail_2) { } -TEST_IMPL(bind_error_fault) { +TEST_IMPL(tcp_bind_error_fault) { char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; struct sockaddr_in* garbage_addr; uv_tcp_t server; @@ -126,18 +120,16 @@ TEST_IMPL(bind_error_fault) { garbage_addr = (struct sockaddr_in*) &garbage; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, *garbage_addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EFAULT); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EFAULT); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -146,26 +138,24 @@ TEST_IMPL(bind_error_fault) { /* Notes: On Linux uv_bind(server, NULL) will segfault the program. */ -TEST_IMPL(bind_error_inval) { +TEST_IMPL(tcp_bind_error_inval) { struct sockaddr_in addr1 = uv_ip4_addr("0.0.0.0", TEST_PORT); struct sockaddr_in addr2 = uv_ip4_addr("0.0.0.0", TEST_PORT_2); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr1); ASSERT(r == 0); r = uv_tcp_bind(&server, addr2); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EINVAL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -173,18 +163,29 @@ TEST_IMPL(bind_error_inval) { } -TEST_IMPL(bind_localhost_ok) { +TEST_IMPL(tcp_bind_localhost_ok) { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr); ASSERT(r == 0); return 0; } + + +TEST_IMPL(tcp_listen_without_bind) { + int r; + uv_tcp_t server; + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server, 128, NULL); + ASSERT(r == 0); + + return 0; +} diff --git a/src/rt/libuv/test/test-bind6-error.c b/src/rt/libuv/test/test-tcp-bind6-error.c similarity index 76% rename from src/rt/libuv/test/test-bind6-error.c rename to src/rt/libuv/test/test-tcp-bind6-error.c index 6072dcdc090..5a8b76363a6 100644 --- a/src/rt/libuv/test/test-bind6-error.c +++ b/src/rt/libuv/test/test-tcp-bind6-error.c @@ -34,34 +34,32 @@ static void close_cb(uv_handle_t* handle) { } -TEST_IMPL(bind6_error_addrinuse) { +TEST_IMPL(tcp_bind6_error_addrinuse) { struct sockaddr_in6 addr = uv_ip6_addr("::", TEST_PORT); uv_tcp_t server1, server2; int r; - uv_init(); - - r = uv_tcp_init(&server1); + r = uv_tcp_init(uv_default_loop(), &server1); ASSERT(r == 0); r = uv_tcp_bind6(&server1, addr); ASSERT(r == 0); - r = uv_tcp_init(&server2); + r = uv_tcp_init(uv_default_loop(), &server2); ASSERT(r == 0); r = uv_tcp_bind6(&server2, addr); ASSERT(r == 0); - r = uv_tcp_listen(&server1, 128, NULL); + r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); - r = uv_tcp_listen(&server2, 128, NULL); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRINUSE); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 2); @@ -69,22 +67,20 @@ TEST_IMPL(bind6_error_addrinuse) { } -TEST_IMPL(bind6_error_addrnotavail) { +TEST_IMPL(tcp_bind6_error_addrnotavail) { struct sockaddr_in6 addr = uv_ip6_addr("4:4:4:4:4:4:4:4", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRNOTAVAIL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -92,7 +88,7 @@ TEST_IMPL(bind6_error_addrnotavail) { } -TEST_IMPL(bind6_error_fault) { +TEST_IMPL(tcp_bind6_error_fault) { char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; struct sockaddr_in6* garbage_addr; uv_tcp_t server; @@ -100,18 +96,16 @@ TEST_IMPL(bind6_error_fault) { garbage_addr = (struct sockaddr_in6*) &garbage; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, *garbage_addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EFAULT); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EFAULT); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -120,26 +114,24 @@ TEST_IMPL(bind6_error_fault) { /* Notes: On Linux uv_bind6(server, NULL) will segfault the program. */ -TEST_IMPL(bind6_error_inval) { +TEST_IMPL(tcp_bind6_error_inval) { struct sockaddr_in6 addr1 = uv_ip6_addr("::", TEST_PORT); struct sockaddr_in6 addr2 = uv_ip6_addr("::", TEST_PORT_2); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr1); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr2); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EINVAL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -147,15 +139,13 @@ TEST_IMPL(bind6_error_inval) { } -TEST_IMPL(bind6_localhost_ok) { +TEST_IMPL(tcp_bind6_localhost_ok) { struct sockaddr_in6 addr = uv_ip6_addr("::1", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr); ASSERT(r == 0); diff --git a/src/rt/libuv/test/test-tcp-close.c b/src/rt/libuv/test/test-tcp-close.c new file mode 100644 index 00000000000..5da8a84f8a2 --- /dev/null +++ b/src/rt/libuv/test/test-tcp-close.c @@ -0,0 +1,129 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include /* memset */ + +#define NUM_WRITE_REQS 32 + +static uv_tcp_t tcp_handle; +static uv_connect_t connect_req; + +static int write_cb_called; +static int close_cb_called; + +static void connect_cb(uv_connect_t* req, int status); +static void write_cb(uv_write_t* req, int status); +static void close_cb(uv_handle_t* handle); + + +static void connect_cb(uv_connect_t* conn_req, int status) { + uv_write_t* req; + uv_buf_t buf; + int i, r; + + buf = uv_buf_init("PING", 4); + for (i = 0; i < NUM_WRITE_REQS; i++) { + req = malloc(sizeof *req); + ASSERT(req != NULL); + + r = uv_write(req, (uv_stream_t*)&tcp_handle, &buf, 1, write_cb); + ASSERT(r == 0); + } + + uv_close((uv_handle_t*)&tcp_handle, close_cb); +} + + +static void write_cb(uv_write_t* req, int status) { + /* write callbacks should run before the close callback */ + ASSERT(close_cb_called == 0); + ASSERT(req->handle == (uv_stream_t*)&tcp_handle); + write_cb_called++; + free(req); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*)&tcp_handle); + close_cb_called++; +} + + +static void connection_cb(uv_stream_t* server, int status) { + ASSERT(status == 0); +} + + +static void start_server(uv_loop_t* loop, uv_tcp_t* handle) { + int r; + + r = uv_tcp_init(loop, handle); + ASSERT(r == 0); + + r = uv_tcp_bind(handle, uv_ip4_addr("127.0.0.1", TEST_PORT)); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)handle, 128, connection_cb); + ASSERT(r == 0); + + uv_unref(loop); +} + + +/* Check that pending write requests have their callbacks + * invoked when the handle is closed. + */ +TEST_IMPL(tcp_close) { + uv_loop_t* loop; + uv_tcp_t tcp_server; + int r; + + loop = uv_default_loop(); + + /* We can't use the echo server, it doesn't handle ECONNRESET. */ + start_server(loop, &tcp_server); + + r = uv_tcp_init(loop, &tcp_handle); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &tcp_handle, + uv_ip4_addr("127.0.0.1", TEST_PORT), + connect_cb); + ASSERT(r == 0); + + ASSERT(write_cb_called == 0); + ASSERT(close_cb_called == 0); + + r = uv_run(loop); + ASSERT(r == 0); + + printf("%d of %d write reqs seen\n", write_cb_called, NUM_WRITE_REQS); + + ASSERT(write_cb_called == NUM_WRITE_REQS); + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-tcp-write-error.c b/src/rt/libuv/test/test-tcp-write-error.c new file mode 100644 index 00000000000..f3d12b8d6bf --- /dev/null +++ b/src/rt/libuv/test/test-tcp-write-error.c @@ -0,0 +1,154 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +static void connection_cb(uv_stream_t* server, int status); +static void connect_cb(uv_connect_t* req, int status); +static void write_cb(uv_write_t* req, int status); +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size); + +static uv_tcp_t tcp_server; +static uv_tcp_t tcp_client; +static uv_tcp_t tcp_peer; /* client socket as accept()-ed by server */ +static uv_write_t write_req; +static uv_connect_t connect_req; + +static int write_cb_called; + + +static void connection_cb(uv_stream_t* server, int status) { + int r; + + ASSERT(server == (uv_stream_t*)&tcp_server); + ASSERT(status == 0); + + r = uv_tcp_init(server->loop, &tcp_peer); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)&tcp_peer); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&tcp_peer, alloc_cb, read_cb); + ASSERT(r == 0); +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[1024]; + return uv_buf_init(slab, sizeof slab); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { + uv_close((uv_handle_t*)&tcp_server, NULL); + uv_close((uv_handle_t*)&tcp_peer, NULL); +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_buf_t buf; + size_t size; + char* data; + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + size = 10*1024*1024; + data = malloc(size); + ASSERT(data != NULL); + + memset(data, '$', size); + buf = uv_buf_init(data, size); + + write_req.data = data; + + r = uv_write(&write_req, req->handle, &buf, 1, write_cb); + ASSERT(r == 0); + + /* Write queue should have been updated. */ + ASSERT(req->handle->write_queue_size > 0); + + /* write_queue_size <= size, part may have already been written. */ + ASSERT(req->handle->write_queue_size <= size); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req == &write_req); + ASSERT(status == -1); + + /* This is what this test is all about. */ + ASSERT(tcp_client.write_queue_size == 0); + + free(write_req.data); + + uv_close((uv_handle_t*)&tcp_client, NULL); + + write_cb_called++; +} + + +/* + * Assert that a failing write does not leave + * the stream's write_queue_size in an inconsistent state. + */ +TEST_IMPL(tcp_write_error) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + r = uv_tcp_init(loop, &tcp_server); + ASSERT(r == 0); + + r = uv_tcp_bind(&tcp_server, uv_ip4_addr("127.0.0.1", TEST_PORT)); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, 1, connection_cb); + ASSERT(r == 0); + + r = uv_tcp_init(loop, &tcp_client); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &tcp_client, + uv_ip4_addr("127.0.0.1", TEST_PORT), + connect_cb); + ASSERT(r == 0); + + ASSERT(write_cb_called == 0); + + r = uv_run(loop); + ASSERT(r == 0); + + ASSERT(write_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-tcp-writealot.c b/src/rt/libuv/test/test-tcp-writealot.c index 4e305a9f33d..f6b1dc6efa1 100644 --- a/src/rt/libuv/test/test-tcp-writealot.c +++ b/src/rt/libuv/test/test-tcp-writealot.c @@ -45,7 +45,7 @@ static int bytes_received = 0; static int bytes_received_done = 0; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -62,7 +62,7 @@ static void close_cb(uv_handle_t* handle) { } -static void shutdown_cb(uv_req_t* req, int status) { +static void shutdown_cb(uv_shutdown_t* req, int status) { uv_tcp_t* tcp; ASSERT(req); @@ -87,7 +87,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { ASSERT(tcp != NULL); if (nread < 0) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); printf("GOT EOF\n"); if (buf.base) { @@ -104,11 +104,11 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { } -static void write_cb(uv_req_t* req, int status) { +static void write_cb(uv_write_t* req, int status) { ASSERT(req != NULL); if (status) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(uv_default_loop()); fprintf(stderr, "uv_write error: %s\n", uv_strerror(err)); ASSERT(0); } @@ -120,9 +120,11 @@ static void write_cb(uv_req_t* req, int status) { } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { uv_buf_t send_bufs[CHUNKS_PER_WRITE]; uv_tcp_t* tcp; + uv_write_t* write_req; + uv_shutdown_t* shutdown_req; int i, j, r; ASSERT(req != NULL); @@ -141,26 +143,21 @@ static void connect_cb(uv_req_t* req, int status) { bytes_sent += CHUNK_SIZE; } - req = (uv_req_t*)malloc(sizeof *req); - ASSERT(req != NULL); + write_req = malloc(sizeof(uv_write_t)); + ASSERT(write_req != NULL); - uv_req_init(req, (uv_handle_t*)tcp, (void *(*)(void *))write_cb); - r = uv_write(req, (uv_buf_t*)&send_bufs, CHUNKS_PER_WRITE); + r = uv_write(write_req, (uv_stream_t*) tcp, (uv_buf_t*)&send_bufs, + CHUNKS_PER_WRITE, write_cb); ASSERT(r == 0); } /* Shutdown on drain. FIXME: dealloc req? */ - req = (uv_req_t*) malloc(sizeof(uv_req_t)); - ASSERT(req != NULL); - uv_req_init(req, (uv_handle_t*)tcp, (void *(*)(void *))shutdown_cb); - r = uv_shutdown(req); + shutdown_req = malloc(sizeof(uv_shutdown_t)); + ASSERT(shutdown_req != NULL); + r = uv_shutdown(shutdown_req, (uv_stream_t*)tcp, shutdown_cb); ASSERT(r == 0); /* Start reading */ - req = (uv_req_t*)malloc(sizeof *req); - ASSERT(req != NULL); - - uv_req_init(req, (uv_handle_t*)tcp, (void *(*)(void *))read_cb); r = uv_read_start((uv_stream_t*)tcp, alloc_cb, read_cb); ASSERT(r == 0); } @@ -169,7 +166,7 @@ static void connect_cb(uv_req_t* req, int status) { TEST_IMPL(tcp_writealot) { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof *client); - uv_req_t* connect_req = (uv_req_t*)malloc(sizeof *connect_req); + uv_connect_t* connect_req = malloc(sizeof(uv_connect_t)); int r; ASSERT(client != NULL); @@ -179,16 +176,13 @@ TEST_IMPL(tcp_writealot) { ASSERT(send_buffer != NULL); - uv_init(); - - r = uv_tcp_init(client); + r = uv_tcp_init(uv_default_loop(), client); ASSERT(r == 0); - uv_req_init(connect_req, (uv_handle_t*)client, (void *(*)(void *))connect_cb); - r = uv_tcp_connect(connect_req, addr); + r = uv_tcp_connect(connect_req, client, addr, connect_cb); ASSERT(r == 0); - uv_run(); + uv_run(uv_default_loop()); ASSERT(shutdown_cb_called == 1); ASSERT(connect_cb_called == 1); diff --git a/src/rt/libuv/test/test-threadpool.c b/src/rt/libuv/test/test-threadpool.c new file mode 100644 index 00000000000..92130b506c5 --- /dev/null +++ b/src/rt/libuv/test/test-threadpool.c @@ -0,0 +1,57 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static int work_cb_count; +static int after_work_cb_count; +static uv_work_t work_req; +static char data; + + +static void work_cb(uv_work_t* req) { + ASSERT(req == &work_req); + ASSERT(req->data == &data); + work_cb_count++; +} + + +static void after_work_cb(uv_work_t* req) { + ASSERT(req == &work_req); + ASSERT(req->data == &data); + after_work_cb_count++; +} + + +TEST_IMPL(threadpool_queue_work_simple) { + int r; + + work_req.data = &data; + r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); + ASSERT(r == 0); + uv_run(uv_default_loop()); + + ASSERT(work_cb_count == 1); + ASSERT(after_work_cb_count == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-timer-again.c b/src/rt/libuv/test/test-timer-again.c index e083b01ced1..9eeee1e34dc 100644 --- a/src/rt/libuv/test/test-timer-again.c +++ b/src/rt/libuv/test/test-timer-again.c @@ -49,14 +49,15 @@ static void repeat_1_cb(uv_timer_t* handle, int status) { ASSERT(uv_timer_get_repeat((uv_timer_t*)handle) == 50); - LOGF("repeat_1_cb called after %ld ms\n", (long int)(uv_now() - start_time)); + LOGF("repeat_1_cb called after %ld ms\n", + (long int)(uv_now(uv_default_loop()) - start_time)); repeat_1_cb_called++; r = uv_timer_again(&repeat_2); ASSERT(r == 0); - if (uv_now() >= start_time + 500) { + if (uv_now(uv_default_loop()) >= start_time + 500) { uv_close((uv_handle_t*)handle, close_cb); /* We're not calling uv_timer_again on repeat_2 any more, so after this */ /* timer_2_cb is expected. */ @@ -71,7 +72,8 @@ static void repeat_2_cb(uv_timer_t* handle, int status) { ASSERT(status == 0); ASSERT(repeat_2_cb_allowed); - LOGF("repeat_2_cb called after %ld ms\n", (long int)(uv_now() - start_time)); + LOGF("repeat_2_cb called after %ld ms\n", + (long int)(uv_now(uv_default_loop()) - start_time)); repeat_2_cb_called++; @@ -93,21 +95,19 @@ static void repeat_2_cb(uv_timer_t* handle, int status) { TEST_IMPL(timer_again) { int r; - uv_init(); - - start_time = uv_now(); + start_time = uv_now(uv_default_loop()); ASSERT(0 < start_time); /* Verify that it is not possible to uv_timer_again a never-started timer. */ - r = uv_timer_init(&dummy); + r = uv_timer_init(uv_default_loop(), &dummy); ASSERT(r == 0); r = uv_timer_again(&dummy); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EINVAL); - uv_unref(); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + uv_unref(uv_default_loop()); /* Start timer repeat_1. */ - r = uv_timer_init(&repeat_1); + r = uv_timer_init(uv_default_loop(), &repeat_1); ASSERT(r == 0); r = uv_timer_start(&repeat_1, repeat_1_cb, 50, 0); ASSERT(r == 0); @@ -121,21 +121,21 @@ TEST_IMPL(timer_again) { * Start another repeating timer. It'll be again()ed by the repeat_1 so * it should not time out until repeat_1 stops. */ - r = uv_timer_init(&repeat_2); + r = uv_timer_init(uv_default_loop(), &repeat_2); ASSERT(r == 0); r = uv_timer_start(&repeat_2, repeat_2_cb, 100, 100); ASSERT(r == 0); ASSERT(uv_timer_get_repeat(&repeat_2) == 100); - uv_run(); + uv_run(uv_default_loop()); ASSERT(repeat_1_cb_called == 10); ASSERT(repeat_2_cb_called == 2); ASSERT(close_cb_called == 2); LOGF("Test took %ld ms (expected ~700 ms)\n", - (long int)(uv_now() - start_time)); - ASSERT(700 <= uv_now() - start_time); + (long int)(uv_now(uv_default_loop()) - start_time)); + ASSERT(700 <= uv_now(uv_default_loop()) - start_time); return 0; } diff --git a/src/rt/libuv/test/test-timer.c b/src/rt/libuv/test/test-timer.c index c62a8c68dbc..17bcb84b770 100644 --- a/src/rt/libuv/test/test-timer.c +++ b/src/rt/libuv/test/test-timer.c @@ -53,7 +53,7 @@ static void once_cb(uv_timer_t* handle, int status) { uv_close((uv_handle_t*)handle, once_close_cb); /* Just call this randomly for the code coverage. */ - uv_update_time(); + uv_update_time(uv_default_loop()); } @@ -90,37 +90,35 @@ TEST_IMPL(timer) { uv_timer_t repeat, never; int i, r; - uv_init(); - - start_time = uv_now(); + start_time = uv_now(uv_default_loop()); ASSERT(0 < start_time); /* Let 10 timers time out in 500 ms total. */ for (i = 0; i < 10; i++) { once = (uv_timer_t*)malloc(sizeof(*once)); ASSERT(once != NULL); - r = uv_timer_init(once); + r = uv_timer_init(uv_default_loop(), once); ASSERT(r == 0); r = uv_timer_start(once, once_cb, i * 50, 0); ASSERT(r == 0); } /* The 11th timer is a repeating timer that runs 4 times */ - r = uv_timer_init(&repeat); + r = uv_timer_init(uv_default_loop(), &repeat); ASSERT(r == 0); r = uv_timer_start(&repeat, repeat_cb, 100, 100); ASSERT(r == 0); /* The 12th timer should not do anything. */ - r = uv_timer_init(&never); + r = uv_timer_init(uv_default_loop(), &never); ASSERT(r == 0); r = uv_timer_start(&never, never_cb, 100, 100); ASSERT(r == 0); r = uv_timer_stop(&never); ASSERT(r == 0); - uv_unref(); + uv_unref(uv_default_loop()); - uv_run(); + uv_run(uv_default_loop()); ASSERT(once_cb_called == 10); ASSERT(once_close_cb_called == 10); @@ -128,7 +126,7 @@ TEST_IMPL(timer) { ASSERT(repeat_cb_called == 5); ASSERT(repeat_close_cb_called == 1); - ASSERT(500 <= uv_now() - start_time); + ASSERT(500 <= uv_now(uv_default_loop()) - start_time); return 0; } diff --git a/src/rt/libuv/test/test-tty.c b/src/rt/libuv/test/test-tty.c new file mode 100644 index 00000000000..11816156fff --- /dev/null +++ b/src/rt/libuv/test/test-tty.c @@ -0,0 +1,56 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +TEST_IMPL(tty) { + int r, width, height; + uv_tty_t tty; + uv_loop_t* loop = uv_default_loop(); + + /* + * Not necessarally a problem if this assert goes off. E.G you are piping + * this test to a file. 0 == stdin. + */ + ASSERT(UV_TTY == uv_guess_handle(0)); + + r = uv_tty_init(uv_default_loop(), &tty, 0); + ASSERT(r == 0); + + r = uv_tty_get_winsize(&tty, &width, &height); + ASSERT(r == 0); + + printf("width=%d height=%d\n", width, height); + + /* + * Is it a safe assumption that most people have terminals larger than + * 10x10? + */ + ASSERT(width > 10); + ASSERT(height > 10); + + uv_close((uv_handle_t*)&tty, NULL); + + uv_run(loop); + + return 0; +} diff --git a/src/rt/libuv/test/test-udp-dgram-too-big.c b/src/rt/libuv/test/test-udp-dgram-too-big.c new file mode 100644 index 00000000000..2d172c0640c --- /dev/null +++ b/src/rt/libuv/test/test-udp-dgram-too-big.c @@ -0,0 +1,86 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &handle_) + +#define CHECK_REQ(req) \ + ASSERT((req) == &req_); + +static uv_udp_t handle_; +static uv_udp_send_t req_; + +static int send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + CHECK_REQ(req); + CHECK_HANDLE(req->handle); + + ASSERT(status == -1); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EMSGSIZE); + + uv_close((uv_handle_t*)req->handle, close_cb); + send_cb_called++; +} + + +TEST_IMPL(udp_dgram_too_big) { + char dgram[65536]; /* 64K MTU is unlikely, even on localhost */ + struct sockaddr_in addr; + uv_buf_t buf; + int r; + + memset(dgram, 42, sizeof dgram); /* silence valgrind */ + + r = uv_udp_init(uv_default_loop(), &handle_); + ASSERT(r == 0); + + buf = uv_buf_init(dgram, sizeof dgram); + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_udp_send(&req_, &handle_, &buf, 1, addr, send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(send_cb_called == 0); + + uv_run(uv_default_loop()); + + ASSERT(send_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-udp-ipv6.c b/src/rt/libuv/test/test-udp-ipv6.c new file mode 100644 index 00000000000..6ff36b32b01 --- /dev/null +++ b/src/rt/libuv/test/test-udp-ipv6.c @@ -0,0 +1,156 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server \ + || (uv_udp_t*)(handle) == &client \ + || (uv_timer_t*)(handle) == &timeout) + +#define CHECK_REQ(req) \ + ASSERT((req) == &req_); + +static uv_udp_t client; +static uv_udp_t server; +static uv_udp_send_t req_; +static uv_timer_t timeout; + +static int send_cb_called; +static int recv_cb_called; +static int close_cb_called; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + CHECK_HANDLE(handle); + return uv_buf_init(slab, sizeof slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + CHECK_REQ(req); + CHECK_HANDLE(req->handle); + ASSERT(status == 0); + send_cb_called++; +} + + +static void ipv6_recv_fail(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + ASSERT(0 && "this function should not have been called"); +} + + +static void ipv6_recv_ok(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(nread >= 0); + + if (nread) + recv_cb_called++; +} + + +static void timeout_cb(uv_timer_t* timer, int status) { + uv_close((uv_handle_t*)&server, close_cb); + uv_close((uv_handle_t*)&client, close_cb); + uv_close((uv_handle_t*)&timeout, close_cb); +} + + +static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { + struct sockaddr_in6 addr6; + struct sockaddr_in addr; + uv_buf_t buf; + int r; + + addr6 = uv_ip6_addr("::0", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind6(&server, addr6, bind_flags); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, recv_cb); + ASSERT(r == 0); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + buf = uv_buf_init("PING", 4); + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_udp_send(&req_, &client, &buf, 1, addr, send_cb); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timeout); + ASSERT(r == 0); + + r = uv_timer_start(&timeout, timeout_cb, 500, 0); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(send_cb_called == 0); + ASSERT(recv_cb_called == 0); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 3); +} + + +TEST_IMPL(udp_dual_stack) { + do_test(ipv6_recv_ok, 0); + + ASSERT(recv_cb_called == 1); + ASSERT(send_cb_called == 1); + + return 0; +} + + +TEST_IMPL(udp_ipv6_only) { + do_test(ipv6_recv_fail, UV_UDP_IPV6ONLY); + + ASSERT(recv_cb_called == 0); + ASSERT(send_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-udp-send-and-recv.c b/src/rt/libuv/test/test-udp-send-and-recv.c new file mode 100644 index 00000000000..ab47e91c21c --- /dev/null +++ b/src/rt/libuv/test/test-udp-send-and-recv.c @@ -0,0 +1,208 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int cl_send_cb_called; +static int cl_recv_cb_called; + +static int sv_send_cb_called; +static int sv_recv_cb_called; + +static int close_cb_called; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof slab); + + return uv_buf_init(slab, sizeof slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void cl_recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards cl_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PONG", buf.base, nread)); + + cl_recv_cb_called++; + + uv_close((uv_handle_t*) handle, close_cb); +} + + +static void cl_send_cb(uv_udp_send_t* req, int status) { + int r; + + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + r = uv_udp_recv_start(req->handle, alloc_cb, cl_recv_cb); + ASSERT(r == 0); + + cl_send_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + uv_close((uv_handle_t*) req->handle, close_cb); + free(req); + + sv_send_cb_called++; +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + uv_udp_send_t* req; + int r; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards sv_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PING", buf.base, nread)); + + /* FIXME? `uv_udp_recv_stop` does what it says: recv_cb is not called + * anymore. That's problematic because the read buffer won't be returned + * either... Not sure I like that but it's consistent with `uv_read_stop`. + */ + r = uv_udp_recv_stop(handle); + ASSERT(r == 0); + + req = malloc(sizeof *req); + ASSERT(req != NULL); + + buf = uv_buf_init("PONG", 4); + + r = uv_udp_send(req, + handle, + &buf, + 1, + *(struct sockaddr_in*)addr, + sv_send_cb); + ASSERT(r == 0); + + sv_recv_cb_called++; +} + + +TEST_IMPL(udp_send_and_recv) { + struct sockaddr_in addr; + uv_udp_send_t req; + uv_buf_t buf; + int r; + + addr = uv_ip4_addr("0.0.0.0", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT(r == 0); + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + /* client sends "PING", expects "PONG" */ + buf = uv_buf_init("PING", 4); + + r = uv_udp_send(&req, &client, &buf, 1, addr, cl_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(cl_send_cb_called == 0); + ASSERT(cl_recv_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + ASSERT(sv_recv_cb_called == 0); + + uv_run(uv_default_loop()); + + ASSERT(cl_send_cb_called == 1); + ASSERT(cl_recv_cb_called == 1); + ASSERT(sv_send_cb_called == 1); + ASSERT(sv_recv_cb_called == 1); + ASSERT(close_cb_called == 2); + + return 0; +} diff --git a/src/rt/libuv/uv.gyp b/src/rt/libuv/uv.gyp new file mode 100644 index 00000000000..e32dddd3b5d --- /dev/null +++ b/src/rt/libuv/uv.gyp @@ -0,0 +1,339 @@ +{ + 'targets': [ + { + 'target_name': 'uv', + 'type': '<(library)', + 'include_dirs': [ + 'include', + 'include/uv-private', + 'src/', + ], + 'direct_dependent_settings': { + 'include_dirs': [ 'include' ], + }, + + 'defines': [ + 'HAVE_CONFIG_H' + ], + 'sources': [ + 'include/ares.h', + 'include/ares_version.h', + 'include/uv.h', + 'src/uv-common.c', + 'src/uv-common.h', + 'src/ares/ares__close_sockets.c', + 'src/ares/ares__get_hostent.c', + 'src/ares/ares__read_line.c', + 'src/ares/ares__timeval.c', + 'src/ares/ares_cancel.c', + 'src/ares/ares_data.c', + 'src/ares/ares_data.h', + 'src/ares/ares_destroy.c', + 'src/ares/ares_dns.h', + 'src/ares/ares_expand_name.c', + 'src/ares/ares_expand_string.c', + 'src/ares/ares_fds.c', + 'src/ares/ares_free_hostent.c', + 'src/ares/ares_free_string.c', + 'src/ares/ares_gethostbyaddr.c', + 'src/ares/ares_gethostbyname.c', + 'src/ares/ares_getnameinfo.c', + 'src/ares/ares_getopt.c', + 'src/ares/ares_getopt.h', + 'src/ares/ares_getsock.c', + 'src/ares/ares_init.c', + 'src/ares/ares_ipv6.h', + 'src/ares/ares_library_init.c', + 'src/ares/ares_library_init.h', + 'src/ares/ares_llist.c', + 'src/ares/ares_llist.h', + 'src/ares/ares_mkquery.c', + 'src/ares/ares_nowarn.c', + 'src/ares/ares_nowarn.h', + 'src/ares/ares_options.c', + 'src/ares/ares_parse_a_reply.c', + 'src/ares/ares_parse_aaaa_reply.c', + 'src/ares/ares_parse_mx_reply.c', + 'src/ares/ares_parse_ns_reply.c', + 'src/ares/ares_parse_ptr_reply.c', + 'src/ares/ares_parse_srv_reply.c', + 'src/ares/ares_parse_txt_reply.c', + 'src/ares/ares_private.h', + 'src/ares/ares_process.c', + 'src/ares/ares_query.c', + 'src/ares/ares_rules.h', + 'src/ares/ares_search.c', + 'src/ares/ares_send.c', + 'src/ares/ares_setup.h', + 'src/ares/ares_strcasecmp.c', + 'src/ares/ares_strcasecmp.h', + 'src/ares/ares_strdup.c', + 'src/ares/ares_strdup.h', + 'src/ares/ares_strerror.c', + 'src/ares/ares_timeout.c', + 'src/ares/ares_version.c', + 'src/ares/ares_writev.c', + 'src/ares/ares_writev.h', + 'src/ares/bitncmp.c', + 'src/ares/bitncmp.h', + 'src/ares/inet_net_pton.c', + 'src/ares/inet_net_pton.h', + 'src/ares/inet_ntop.c', + 'src/ares/inet_ntop.h', + 'src/ares/nameser.h', + 'src/ares/setup_once.h', + ], + 'conditions': [ + [ 'OS=="win"', { + 'include_dirs': [ + 'src/ares/config_win32' + ], + 'defines': [ + '_WIN32_WINNT=0x0502', + 'EIO_STACKSIZE=262144', + '_GNU_SOURCE', + ], + 'sources': [ + 'include/uv-private/tree.h', + 'include/uv-private/uv-win.h', + 'src/ares/config_win32/ares_config.h', + 'src/ares/windows_port.c', + 'src/win/async.c', + 'src/win/cares.c', + 'src/win/core.c', + 'src/win/error.c', + 'src/win/fs.c', + 'src/win/fs-event.c', + 'src/win/getaddrinfo.c', + 'src/win/handle.c', + 'src/win/internal.h', + 'src/win/loop-watcher.c', + 'src/win/pipe.c', + 'src/win/process.c', + 'src/win/req.c', + 'src/win/stdio.c', + 'src/win/stream.c', + 'src/win/tcp.c', + 'src/win/tty.c', + 'src/win/threadpool.c', + 'src/win/threads.c', + 'src/win/timer.c', + 'src/win/udp.c', + 'src/win/util.c', + 'src/win/winapi.c', + 'src/win/winapi.h', + 'src/win/winsock.c', + 'src/win/winsock.h', + ], + 'link_settings': { + 'libraries': [ + '-lws2_32.lib', + ], + }, + }, { # Not Windows i.e. POSIX + 'cflags': [ + '-g', + '--std=gnu89', + '-pedantic', + '-Wall', + '-Wextra', + '-Wno-unused-parameter' + ], + 'sources': [ + 'include/uv-private/eio.h', + 'include/uv-private/ev.h', + 'include/uv-private/ngx-queue.h', + 'include/uv-private/uv-unix.h', + 'src/unix/core.c', + 'src/unix/uv-eio.c', + 'src/unix/uv-eio.h', + 'src/unix/fs.c', + 'src/unix/udp.c', + 'src/unix/tcp.c', + 'src/unix/pipe.c', + 'src/unix/tty.c', + 'src/unix/stream.c', + 'src/unix/cares.c', + 'src/unix/error.c', + 'src/unix/process.c', + 'src/unix/internal.h', + 'src/unix/eio/ecb.h', + 'src/unix/eio/eio.c', + 'src/unix/eio/xthread.h', + 'src/unix/ev/ev.c', + 'src/unix/ev/ev_vars.h', + 'src/unix/ev/ev_wrap.h', + 'src/unix/ev/event.h', + ], + 'include_dirs': [ 'src/unix/ev', ], + 'defines': [ + '_LARGEFILE_SOURCE', + '_FILE_OFFSET_BITS=64', + '_GNU_SOURCE', + 'EIO_STACKSIZE=262144' + ], + 'libraries': [ '-lm' ] + }], + [ 'OS=="mac"', { + 'include_dirs': [ 'src/ares/config_darwin' ], + 'sources': [ 'src/unix/darwin.c' ], + 'direct_dependent_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Carbon.framework', + '$(SDKROOT)/System/Library/Frameworks/CoreServices.framework', + ], + }, + 'defines': [ + 'EV_CONFIG_H="config_darwin.h"', + 'EIO_CONFIG_H="config_darwin.h"', + ] + }], + [ 'OS=="linux"', { + 'include_dirs': [ 'src/ares/config_linux' ], + 'sources': [ 'src/unix/linux.c' ], + 'defines': [ + 'EV_CONFIG_H="config_linux.h"', + 'EIO_CONFIG_H="config_linux.h"', + ], + 'direct_dependent_settings': { + 'libraries': [ '-lrt' ], + }, + }], + [ 'OS=="solaris"', { + 'include_dirs': [ 'src/ares/config_sunos' ], + 'sources': [ 'src/unix/sunos.c' ], + 'defines': [ + '__EXTENSIONS__', + '_XOPEN_SOURCE=500', + 'EV_CONFIG_H="config_sunos.h"', + 'EIO_CONFIG_H="config_sunos.h"', + ], + 'direct_dependent_settings': { + 'libraries': [ '-lrt' ], + }, + }], + [ 'OS=="freebsd"', { + 'include_dirs': [ 'src/ares/config_freebsd' ], + 'sources': [ 'src/unix/freebsd.c' ], + 'defines': [ + 'EV_CONFIG_H="config_freebsd.h"', + 'EIO_CONFIG_H="config_freebsd.h"', + ], + }], + ] + }, + + { + 'target_name': 'run-tests', + 'type': 'executable', + 'dependencies': [ 'uv' ], + 'sources': [ + 'test/echo-server.c', + 'test/run-tests.c', + 'test/runner.c', + 'test/runner.h', + 'test/task.h', + 'test/test-async.c', + 'test/test-callback-stack.c', + 'test/test-connection-fail.c', + 'test/test-delayed-accept.c', + 'test/test-fail-always.c', + 'test/test-fs.c', + 'test/test-fs-event.c', + 'test/test-get-currentexe.c', + 'test/test-getaddrinfo.c', + 'test/test-gethostbyname.c', + 'test/test-getsockname.c', + 'test/test-hrtime.c', + 'test/test-idle.c', + 'test/test-list.h', + 'test/test-loop-handles.c', + 'test/test-pass-always.c', + 'test/test-ping-pong.c', + 'test/test-pipe-bind-error.c', + 'test/test-ref.c', + 'test/test-shutdown-eof.c', + 'test/test-spawn.c', + 'test/test-tcp-bind-error.c', + 'test/test-tcp-bind6-error.c', + 'test/test-tcp-close.c', + 'test/test-tcp-write-error.c', + 'test/test-tcp-writealot.c', + 'test/test-threadpool.c', + 'test/test-timer-again.c', + 'test/test-timer.c', + 'test/test-tty.c', + 'test/test-udp-dgram-too-big.c', + 'test/test-udp-ipv6.c', + 'test/test-udp-send-and-recv.c', + ], + 'conditions': [ + [ 'OS=="win"', { + 'sources': [ + 'test/runner-win.c', + 'test/runner-win.h' + ], + 'libraries': [ 'ws2_32.lib' ] + }, { # POSIX + 'defines': [ '_GNU_SOURCE' ], + 'ldflags': [ '-pthread' ], + 'sources': [ + 'test/runner-unix.c', + 'test/runner-unix.h', + ] + }] + ], + 'msvs-settings': { + 'VCLinkerTool': { + 'SubSystem': 1, # /subsystem:console + }, + }, + }, + + { + 'target_name': 'run-benchmarks', + 'type': 'executable', + 'dependencies': [ 'uv' ], + 'sources': [ + 'test/benchmark-ares.c', + 'test/benchmark-getaddrinfo.c', + 'test/benchmark-list.h', + 'test/benchmark-ping-pongs.c', + 'test/benchmark-pound.c', + 'test/benchmark-pump.c', + 'test/benchmark-sizes.c', + 'test/benchmark-spawn.c', + 'test/benchmark-udp-packet-storm.c', + 'test/dns-server.c', + 'test/echo-server.c', + 'test/run-benchmarks.c', + 'test/runner.c', + 'test/runner.h', + 'test/task.h', + ], + 'conditions': [ + [ 'OS=="win"', { + 'sources': [ + 'test/runner-win.c', + 'test/runner-win.h', + ], + 'libraries': [ 'ws2_32.lib' ] + }, { # POSIX + 'defines': [ '_GNU_SOURCE' ], + 'ldflags': [ '-pthread' ], + 'sources': [ + 'test/runner-unix.c', + 'test/runner-unix.h', + ] + }] + ], + 'msvs-settings': { + 'VCLinkerTool': { + 'SubSystem': 1, # /subsystem:console + }, + }, + } + ] +} + + diff --git a/src/rt/libuv/vcbuild.bat b/src/rt/libuv/vcbuild.bat new file mode 100644 index 00000000000..bccefff9dcd --- /dev/null +++ b/src/rt/libuv/vcbuild.bat @@ -0,0 +1,93 @@ +@echo off + +cd %~dp0 + +if /i "%1"=="help" goto help +if /i "%1"=="--help" goto help +if /i "%1"=="-help" goto help +if /i "%1"=="/help" goto help +if /i "%1"=="?" goto help +if /i "%1"=="-?" goto help +if /i "%1"=="--?" goto help +if /i "%1"=="/?" goto help + +@rem Process arguments. +set config= +set target=Build +set noprojgen= +set nobuild= +set run= + +:next-arg +if "%1"=="" goto args-done +if /i "%1"=="debug" set config=Debug&goto arg-ok +if /i "%1"=="release" set config=Release&goto arg-ok +if /i "%1"=="test" set run=run-tests.exe&goto arg-ok +if /i "%1"=="bench" set run=run-benchmarks.exe&goto arg-ok +if /i "%1"=="clean" set target=Clean&goto arg-ok +if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok +if /i "%1"=="nobuild" set nobuild=1&goto arg-ok +:arg-ok +shift +goto next-arg +:args-done + +if not "%config%"=="" goto project-gen +if "%run%"=="run-tests.exe" set config=Debug& goto project-gen +if "%run%"=="run-benchmarks.exe" set config=Release& goto project-gen +set config=Debug + +:project-gen +@rem Skip project generation if requested. +if defined noprojgen goto msbuild + +@rem Generate the VS project. + +if exist build\gyp goto have_gyp +echo svn co http://gyp.googlecode.com/svn/trunk@983 build/gyp +svn co http://gyp.googlecode.com/svn/trunk@983 build/gyp +if errorlevel 1 goto gyp_install_failed +goto have_gyp + +:gyp_install_failed +echo Failed to download gyp. Make sure you have subversion installed, or +echo manually install gyp into %~dp0build\gyp. +goto exit + +:have_gyp +python gyp_uv +if errorlevel 1 goto create-msvs-files-failed +if not exist uv.sln goto create-msvs-files-failed +echo Project files generated. + +:msbuild +@rem Skip project generation if requested. +if defined nobuild goto run + +if not defined VCINSTALLDIR echo Build skipped. To build, this file needs to run from VS cmd prompt.& goto run + +@rem Build the sln with msbuild. +msbuild uv.sln /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +if errorlevel 1 goto exit + +:run +@rem Run tests if requested. +if "%run%"=="" goto exit +if not exist %config%\%run% goto exit +echo running '%config%\%run%' +%config%\%run% +goto exit + +:create-msvs-files-failed +echo Failed to create vc project files. +goto exit + +:help +echo vcbuild.bat [debug/release] [test/bench] [clean] [noprojgen] [nobuild] +echo Examples: +echo vcbuild.bat : builds debug build +echo vcbuild.bat test : builds debug build and runs tests +echo vcbuild.bat release bench: builds release build and runs benchmarks +goto exit + +:exit diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index add0e8da455..39f99b9b190 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -1,10 +1,11 @@ -#include "rust_internal.h" -#include "rust_upcall.h" // Disable libev prototypes - they will make inline compatability functions // which are unused and so trigger a warning in gcc since -Wall is on. #define EV_PROTOTYPES 0 #include "uv.h" +#include "rust_internal.h" +#include "rust_upcall.h" + #ifdef __GNUC__ #define LOG_CALLBACK_ENTRY(p) \ LOG(iotask, callback, "> IO CALLBACK %s %p", __FUNCTION__, p) @@ -37,22 +38,7 @@ struct socket_data : public task_owned { if (reader) reader->deref(); } -}; -struct request : public uv_req_t, public task_owned { - rust_task *task; - // Used for notifying about completion of connections, writes - rust_chan *chan; - request(socket_data *data, rust_chan *chan, - void (*cb)(request *req, int status)) { - uv_req_init(this, (uv_handle_t*)&data->socket, (void*(*)(void*))cb); - this->data = data; - this->task = data->task; - this->chan = chan->clone(iotask); - } - socket_data *socket() { - return (socket_data*)data; - } void send_result(void *data) { chan->send(&data); chan->deref(); @@ -60,6 +46,9 @@ struct request : public uv_req_t, public task_owned { } }; +struct req_connect : public uv_connect_t, public task_owned {}; +struct req_write : public uv_write_t, public task_owned {}; + extern "C" CDECL void aio_close_socket(rust_task *task, socket_data *); static uv_idle_s idle_handler; @@ -72,15 +61,14 @@ static void idle_callback(uv_idle_t* handle, int status) { extern "C" CDECL void aio_init(rust_task *task) { LOG_UPCALL_ENTRY(task); iotask = task; - uv_init(); - uv_idle_init(&idle_handler); + uv_idle_init(uv_default_loop(), &idle_handler); uv_idle_start(&idle_handler, idle_callback); } extern "C" CDECL void aio_run(rust_task *task) { LOG_UPCALL_ENTRY(task); idle_handler.data = task; - uv_run(); + uv_run(uv_default_loop()); } void nop_close(uv_handle_t* handle) {} @@ -93,9 +81,10 @@ extern "C" CDECL void aio_stop(rust_task *task) { static socket_data *make_socket(rust_task *task, rust_chan *chan) { socket_data *data = new (task, "make_socket") socket_data; if (!data || - uv_tcp_init(&data->socket)) { + uv_tcp_init(uv_default_loop(), &data->socket)) { return NULL; } + data->socket.data = data; data->task = task; // Connections from servers don't have a channel if (chan) { @@ -110,7 +99,7 @@ static socket_data *make_socket(rust_task *task, rust_chan *chan) { // We allocate the requested space + rust_vec but return a pointer at a // +rust_vec offset so that it writes the bytes to the correct location. -static uv_buf_t alloc_buffer(uv_stream_t *socket, size_t suggested_size) { +static uv_buf_t alloc_buffer(uv_handle_t *socket, size_t suggested_size) { LOG_CALLBACK_ENTRY(socket); uv_buf_t buf; size_t actual_size = suggested_size + sizeof (rust_vec); @@ -148,7 +137,7 @@ static void read_progress(uv_stream_t *socket, ssize_t nread, uv_buf_t buf) { data->reader->send(v); } -static void new_connection(uv_handle_t *socket, int status) { +static void new_connection(uv_stream_t *socket, int status) { LOG_CALLBACK_ENTRY(socket); socket_data *server = (socket_data*)socket->data; I(server->task->sched, (uv_tcp_t*)socket == &server->socket); @@ -176,7 +165,7 @@ extern "C" CDECL socket_data *aio_serve(rust_task *task, const char *ip, if (!server) goto oom; if (uv_tcp_bind(&server->socket, addr) || - uv_tcp_listen(&server->socket, 128, new_connection)) { + uv_listen((uv_stream_t*)&server->socket, 128, new_connection)) { aio_close_socket(task, server); chan->deref(); return NULL; @@ -198,7 +187,7 @@ static void free_socket(uv_handle_t *handle) { // reading and should send the close notification. if (data->reader) { if (data->reader->is_associated()) { - uv_buf_t buf = alloc_buffer((uv_stream_t*)socket, 0); + uv_buf_t buf = alloc_buffer((uv_handle_t*)socket, 0); read_progress((uv_stream_t*)socket, -1, buf); uv_read_stop((uv_stream_t*)socket); } @@ -214,9 +203,7 @@ static void free_socket(uv_handle_t *handle) { extern "C" CDECL void aio_close_socket(rust_task *task, socket_data *client) { LOG_UPCALL_ENTRY(task); - if (uv_close((uv_handle_t*)&client->socket, free_socket)) { - task->fail(); - } + uv_close((uv_handle_t*)&client->socket, free_socket); } extern "C" CDECL void aio_close_server(rust_task *task, socket_data *server, @@ -240,33 +227,35 @@ extern "C" CDECL bool aio_is_null_client(rust_task *task, return server == NULL; } -static void connection_complete(request *req, int status) { +static void connection_complete(uv_connect_t *req, int status) { LOG_CALLBACK_ENTRY(socket); - socket_data *client = req->socket(); - req->send_result(client); - delete req; + socket_data *client = (socket_data*)req->data; + client->send_result(client); + free(req); } extern "C" CDECL void aio_connect(rust_task *task, const char *host, int port, chan_handle *_chan) { LOG_UPCALL_ENTRY(task); rust_chan *chan = task->get_chan_by_handle(_chan); + uv_connect_t *req = NULL; if(!chan) return; struct sockaddr_in addr = uv_ip4_addr(const_cast(host), port); - request *req; socket_data *client = make_socket(iotask, NULL); if (!client) { goto oom_client; } - req = new (client->task, "connection request") - request(client, chan, connection_complete); + req = (uv_connect_t*)client->task->malloc( + sizeof(uv_connect_t), "connection request"); if (!req) { goto oom_req; } - if (0 == uv_tcp_connect(req, addr)) { + req->data = client; + if (0 == uv_tcp_connect(req, &client->socket, addr, connection_complete)) { chan->deref(); return; } + free(req); oom_req: aio_close_socket(task, client); oom_client: @@ -275,11 +264,12 @@ oom_client: return; } -static void write_complete(request *req, int status) { +static void write_complete(uv_write_t *req, int status) { LOG_CALLBACK_ENTRY(socket); bool success = status == 0; - req->send_result(&success); - delete req; + socket_data *client = (socket_data*)req->data; + client->send_result(&success); + free(req); } extern "C" CDECL void aio_writedata(rust_task *task, socket_data *data, @@ -287,6 +277,7 @@ extern "C" CDECL void aio_writedata(rust_task *task, socket_data *data, chan_handle *_chan) { LOG_UPCALL_ENTRY(task); rust_chan *chan = task->get_chan_by_handle(_chan); + uv_write_t *req; if(!chan) return; // uv_buf_t is defined backwards on win32... @@ -297,13 +288,14 @@ extern "C" CDECL void aio_writedata(rust_task *task, socket_data *data, uv_buf_t buffer = { buf, size }; #endif - request *req = new (data->task, "write request") - request(data, chan, write_complete); + req = (uv_write_t*)data->task->malloc(sizeof(uv_write_t), "write request"); if (!req) { goto fail; } - if (uv_write(req, &buffer, 1)) { - delete req; + req->data = data; + if (uv_write(req, (uv_stream_t*)&data->socket, &buffer, 1, + write_complete)) { + free(req); goto fail; } chan->deref();