From e49429ebd2fe0b7c24e2ee533311dd97f6febd1f Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 7 Aug 2014 15:03:17 +0400 Subject: deps: update libuv to v0.11.28 --- deps/uv/.mailmap | 4 + deps/uv/AUTHORS | 9 + deps/uv/ChangeLog | 91 ++- deps/uv/Makefile.am | 12 +- deps/uv/Makefile.mingw | 3 +- deps/uv/README.md | 7 +- deps/uv/configure.ac | 2 +- deps/uv/gyp_uv.py | 12 + deps/uv/img/banner.png | Bin 0 -> 44102 bytes deps/uv/img/logos.svg | 152 +++++ deps/uv/include/uv-aix.h | 32 + deps/uv/include/uv-threadpool.h | 37 ++ deps/uv/include/uv-unix.h | 13 +- deps/uv/include/uv-version.h | 2 +- deps/uv/include/uv-win.h | 22 +- deps/uv/include/uv.h | 414 +++++++------ deps/uv/src/threadpool.c | 303 ++++++++++ deps/uv/src/unix/aix.c | 878 +++++++++++++++++++++++++++- deps/uv/src/unix/core.c | 7 +- deps/uv/src/unix/fs.c | 30 +- deps/uv/src/unix/internal.h | 15 +- deps/uv/src/unix/loop.c | 4 +- deps/uv/src/unix/openbsd.c | 30 +- deps/uv/src/unix/stream.c | 65 +- deps/uv/src/unix/threadpool.c | 284 --------- deps/uv/src/unix/udp.c | 213 ++++--- deps/uv/src/uv-common.c | 33 ++ deps/uv/src/uv-common.h | 15 + deps/uv/src/win/core.c | 161 +++-- deps/uv/src/win/error.c | 1 + deps/uv/src/win/fs.c | 177 ++++-- deps/uv/src/win/getaddrinfo.c | 63 +- deps/uv/src/win/getnameinfo.c | 61 +- deps/uv/src/win/internal.h | 35 +- deps/uv/src/win/pipe.c | 64 +- deps/uv/src/win/process.c | 280 ++++++--- deps/uv/src/win/req-inl.h | 16 - deps/uv/src/win/stream-inl.h | 11 - deps/uv/src/win/tcp.c | 65 +- deps/uv/src/win/threadpool.c | 81 --- deps/uv/src/win/timer.c | 50 +- deps/uv/src/win/tty.c | 4 + deps/uv/src/win/udp.c | 27 +- deps/uv/test/run-tests.c | 5 + deps/uv/test/test-barrier.c | 29 +- deps/uv/test/test-fs.c | 67 +++ deps/uv/test/test-getsockname.c | 3 + deps/uv/test/test-ipc.c | 125 +++- deps/uv/test/test-list.h | 19 + deps/uv/test/test-loop-time.c | 29 + deps/uv/test/test-spawn.c | 151 ++++- deps/uv/test/test-tcp-close-accept.c | 5 + deps/uv/test/test-tcp-try-write.c | 12 +- deps/uv/test/test-tcp-write-queue-order.c | 137 +++++ deps/uv/test/test-udp-multicast-interface.c | 3 + deps/uv/test/test-udp-open.c | 2 + deps/uv/test/test-udp-send-and-recv.c | 3 + deps/uv/test/test-udp-send-immediate.c | 148 +++++ deps/uv/test/test-udp-try-send.c | 133 +++++ deps/uv/test/test-watcher-cross-stop.c | 2 +- deps/uv/uv.gyp | 23 +- 61 files changed, 3498 insertions(+), 1183 deletions(-) create mode 100644 deps/uv/img/banner.png create mode 100644 deps/uv/img/logos.svg create mode 100644 deps/uv/include/uv-aix.h create mode 100644 deps/uv/include/uv-threadpool.h create mode 100644 deps/uv/src/threadpool.c delete mode 100644 deps/uv/src/unix/threadpool.c delete mode 100644 deps/uv/src/win/threadpool.c create mode 100644 deps/uv/test/test-tcp-write-queue-order.c create mode 100644 deps/uv/test/test-udp-send-immediate.c create mode 100644 deps/uv/test/test-udp-try-send.c (limited to 'deps') diff --git a/deps/uv/.mailmap b/deps/uv/.mailmap index 89c1adec4d..2ca07c8381 100644 --- a/deps/uv/.mailmap +++ b/deps/uv/.mailmap @@ -1,3 +1,4 @@ +Aaron Bieber Alan Gutierrez Andrius Bentkus Bert Belder @@ -5,6 +6,7 @@ Bert Belder Brandon Philips Brian White Brian White +Caleb James DeLisle Christoph Iserlohn Fedor Indutny Frank Denis @@ -15,6 +17,8 @@ Keno Fischer Maciej Małecki Marc Schlaich Rasmus Christian Pedersen +Rasmus Christian Pedersen +Rasmus Christian Pedersen Rasmus Christian Pedersen Rasmus Pedersen Robert Mustacchi diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index e3de576233..19f911f113 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -146,3 +146,12 @@ HungMingWu Jay Satiro Leith Bade Peter Atashian +Tim Cooper +Caleb James DeLisle +Jameson Nash +Graham Lee +Andrew Low +Pavel Platto +Tony Kelman +John Firebaugh +lilohuang diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 50d39893a2..db13f188c6 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,93 @@ -2014.06.28, Version 0.11.26 (Unstable) +2014.08.08, Version 0.11.28 (Unstable) + +Changes since version 0.11.27: + +* unix, windows: const-ify handle in uv_udp_getsockname (Rasmus Pedersen) + +* windows: use UV_ECANCELED for aborted TCP writes (Saúl Ibarra Corretgé) + +* windows: add more required environment variables (Jameson Nash) + +* windows: sort environment variables before calling CreateProcess (Jameson + Nash) + +* unix, windows: move uv_loop_close out of assert (John Firebaugh) + +* windows: fix buffer overflow on uv__getnameinfo_work() (lilohuang) + +* windows: add uv_backend_timeout (Jameson Nash) + +* test: disable tcp_close_accept on Windows (Saúl Ibarra Corretgé) + +* windows: read the PATH env var of the child (Alex Crichton) + +* include: avoid using C++ 'template' reserved word (Iñaki Baz Castillo) + +* include: fix version number (Saúl Ibarra Corretgé) + + +2014.07.32, Version 0.11.27 (Unstable), ffe24f955032d060968ea0289af365006afed55e + +Changes since version 0.11.26: + +* unix, windows: use the same threadpool implementation (Saúl Ibarra Corretgé) + +* unix: use struct sockaddr_storage for target UDP addr (Saúl Ibarra Corretgé) + +* doc: add documentation to uv_udp_start_recv (Andrius Bentkus) + +* common: use common uv__count_bufs code (Andrius Bentkus) + +* unix, win: add send_queue_size and send_queue_count to uv_udp_t (Andrius + Bentkus) + +* unix, win: add uv_udp_try_send (Andrius Bentkus) + +* unix: return UV_EAGAIN if uv_try_write cannot write any data (Saúl Ibarra + Corretgé) + +* windows: fix compatibility with cygwin pipes (Jameson Nash) + +* windows: count queued bytes even if request completed immediately (Saúl + Ibarra Corretgé) + +* windows: disable CRT debug handler on MinGW32 (Saúl Ibarra Corretgé) + +* windows: map ERROR_INVALID_DRIVE to UV_ENOENT (Saúl Ibarra Corretgé) + +* unix: try to write immediately in uv_udp_send (Saúl Ibarra Corretgé) + +* unix: remove incorrect assert (Saúl Ibarra Corretgé) + +* openbsd: avoid requiring privileges for uv_resident_set_memory (Aaron Bieber) + +* unix: guarantee write queue cb execution order in streams (Andrius Bentkus) + +* img: add logo files (Saúl Ibarra Corretgé) + +* aix: improve AIX compatibility (Andrew Low) + +* windows: return bind error immediately when implicitly binding (Saúl Ibarra + Corretgé) + +* windows: don't use atexit for cleaning up the threadpool (Saúl Ibarra + Corretgé) + +* windows: destroy work queue elements when colsing a loop (Saúl Ibarra + Corretgé) + +* unix, windows: add uv_fs_mkdtemp (Pavel Platto) + +* build: handle platforms without multiprocessing.synchronize (Saúl Ibarra + Corretgé) + +* windows: change GENERIC_ALL to GENERIC_WRITE in fs__create_junction (Tony + Kelman) + +* windows: relay TCP bind errors via ipc (Alexis Campailla) + + +2014.06.28, Version 0.11.26 (Unstable), 115281a1058c4034d5c5ccedacb667fe3f6327ea Changes since version 0.11.25: diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index ebcd8db2e2..861b632bbf 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -17,7 +17,7 @@ ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(top_srcdir)/include \ -I$(top_srcdir)/src -include_HEADERS=include/uv.h include/uv-errno.h include/uv-version.h +include_HEADERS=include/uv.h include/uv-errno.h include/uv-threadpool.h include/uv-version.h CLEANFILES = @@ -28,6 +28,7 @@ libuv_la_SOURCES = src/fs-poll.c \ src/heap-inl.h \ src/inet.c \ src/queue.h \ + src/threadpool.c \ src/uv-common.c \ src/uv-common.h \ src/version.c @@ -67,7 +68,6 @@ libuv_la_SOURCES += src/win/async.c \ src/win/stream-inl.h \ src/win/tcp.c \ src/win/thread.c \ - src/win/threadpool.c \ src/win/timer.c \ src/win/tty.c \ src/win/udp.c \ @@ -99,7 +99,6 @@ libuv_la_SOURCES += src/unix/async.c \ src/unix/stream.c \ src/unix/tcp.c \ src/unix/thread.c \ - src/unix/threadpool.c \ src/unix/timer.c \ src/unix/tty.c \ src/unix/udp.c @@ -197,6 +196,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-tcp-write-to-half-open-connection.c \ test/test-tcp-writealot.c \ test/test-tcp-try-write.c \ + test/test-tcp-write-queue-order.c \ test/test-thread.c \ test/test-threadpool-cancel.c \ test/test-threadpool.c \ @@ -215,6 +215,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-udp-open.c \ test/test-udp-options.c \ test/test-udp-send-and-recv.c \ + test/test-udp-send-immediate.c \ + test/test-udp-try-send.c \ test/test-walk-handles.c \ test/test-watcher-cross-stop.c test_run_tests_LDADD = libuv.la @@ -228,7 +230,7 @@ test_run_tests_SOURCES += test/runner-unix.c \ endif if AIX -test_run_tests_CFLAGS += -D_ALL_SOURCE -D_XOPEN_SOURCE=500 +test_run_tests_CFLAGS += -D_ALL_SOURCE -D_XOPEN_SOURCE=500 -D_LINUX_SOURCE_COMPAT endif if SUNOS @@ -237,7 +239,7 @@ endif if AIX -libuv_la_CFLAGS += -D_ALL_SOURCE -D_XOPEN_SOURCE=500 +libuv_la_CFLAGS += -D_ALL_SOURCE -D_XOPEN_SOURCE=500 -D_LINUX_SOURCE_COMPAT libuv_la_SOURCES += src/unix/aix.c endif diff --git a/deps/uv/Makefile.mingw b/deps/uv/Makefile.mingw index a0b7cc941f..156f15dab1 100644 --- a/deps/uv/Makefile.mingw +++ b/deps/uv/Makefile.mingw @@ -26,6 +26,7 @@ CFLAGS += -Wall \ INCLUDES = include/stdint-msvc2008.h \ include/tree.h \ include/uv-errno.h \ + include/uv-threadpool.h \ include/uv-version.h \ include/uv-win.h \ include/uv.h \ @@ -42,6 +43,7 @@ INCLUDES = include/stdint-msvc2008.h \ OBJS = src/fs-poll.o \ src/inet.o \ + src/threadpool.o \ src/uv-common.o \ src/version.o \ src/win/async.o \ @@ -63,7 +65,6 @@ OBJS = src/fs-poll.o \ src/win/stream.o \ src/win/tcp.o \ src/win/thread.o \ - src/win/threadpool.o \ src/win/timer.o \ src/win/tty.o \ src/win/udp.o \ diff --git a/deps/uv/README.md b/deps/uv/README.md index e0e73591d5..364cf695c4 100644 --- a/deps/uv/README.md +++ b/deps/uv/README.md @@ -1,10 +1,12 @@ -# libuv +![libuv][libuv_banner] + +## Overview libuv is a multi-platform support library with a focus on asynchronous I/O. It was primarily developed for use by [Node.js](http://nodejs.org), but it's also used by Mozilla's [Rust language](http://www.rust-lang.org/), [Luvit](http://luvit.io/), [Julia](http://julialang.org/), -[pyuv](https://crate.io/packages/pyuv/), and [others](https://github.com/joyent/libuv/wiki/Projects-that-use-libuv). +[pyuv](https://github.com/saghul/pyuv), and [others](https://github.com/joyent/libuv/wiki/Projects-that-use-libuv). ## Feature highlights @@ -141,3 +143,4 @@ See the [guidelines for contributing][]. [Python]: https://www.python.org/downloads/ [Visual Studio Express 2010]: http://www.microsoft.com/visualstudio/eng/products/visual-studio-2010-express [guidelines for contributing]: https://github.com/joyent/libuv/blob/master/CONTRIBUTING.md +[libuv_banner]: https://raw.githubusercontent.com/joyent/libuv/master/img/banner.png diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index 0423c8cea8..ac789524b5 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [0.11.26], [https://github.com/joyent/libuv/issues]) +AC_INIT([libuv], [0.11.28], [https://github.com/joyent/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) diff --git a/deps/uv/gyp_uv.py b/deps/uv/gyp_uv.py index f0c59d0786..f5afc6da2d 100755 --- a/deps/uv/gyp_uv.py +++ b/deps/uv/gyp_uv.py @@ -6,6 +6,13 @@ import os import subprocess import sys +try: + import multiprocessing.synchronize + gyp_parallel_support = True +except ImportError: + gyp_parallel_support = False + + CC = os.environ.get('CC', 'cc') script_dir = os.path.dirname(__file__) uv_root = os.path.normpath(script_dir) @@ -94,6 +101,11 @@ if __name__ == '__main__': if not any(a.startswith('-Dcomponent=') for a in args): args.append('-Dcomponent=static_library') + # Some platforms (OpenBSD for example) don't have multiprocessing.synchronize + # so gyp must be run with --no-parallel + if not gyp_parallel_support: + args.append('--no-parallel') + gyp_args = list(args) print gyp_args run_gyp(gyp_args) diff --git a/deps/uv/img/banner.png b/deps/uv/img/banner.png new file mode 100644 index 0000000000..7187daa2e5 Binary files /dev/null and b/deps/uv/img/banner.png differ diff --git a/deps/uv/img/logos.svg b/deps/uv/img/logos.svg new file mode 100644 index 0000000000..d6185f8b19 --- /dev/null +++ b/deps/uv/img/logos.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deps/uv/include/uv-aix.h b/deps/uv/include/uv-aix.h new file mode 100644 index 0000000000..7dc992fa6d --- /dev/null +++ b/deps/uv/include/uv-aix.h @@ -0,0 +1,32 @@ +/* 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_AIX_H +#define UV_AIX_H + +#define UV_PLATFORM_LOOP_FIELDS \ + int fs_fd; \ + +#define UV_PLATFORM_FS_EVENT_FIELDS \ + uv__io_t event_watcher; \ + char *dir_filename; \ + +#endif /* UV_AIX_H */ diff --git a/deps/uv/include/uv-threadpool.h b/deps/uv/include/uv-threadpool.h new file mode 100644 index 0000000000..9708ebdd53 --- /dev/null +++ b/deps/uv/include/uv-threadpool.h @@ -0,0 +1,37 @@ +/* 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 file is private to libuv. It provides common functionality to both + * Windows and Unix backends. + */ + +#ifndef UV_THREADPOOL_H_ +#define UV_THREADPOOL_H_ + +struct uv__work { + void (*work)(struct uv__work *w); + void (*done)(struct uv__work *w, int status); + struct uv_loop_s* loop; + void* wq[2]; +}; + +#endif /* UV_THREADPOOL_H_ */ diff --git a/deps/uv/include/uv-unix.h b/deps/uv/include/uv-unix.h index d59d743ef2..bbaaefc3ed 100644 --- a/deps/uv/include/uv-unix.h +++ b/deps/uv/include/uv-unix.h @@ -42,8 +42,12 @@ #endif #include +#include "uv-threadpool.h" + #if defined(__linux__) # include "uv-linux.h" +#elif defined(_AIX) +# include "uv-aix.h" #elif defined(__sun) # include "uv-sunos.h" #elif defined(__APPLE__) @@ -96,13 +100,6 @@ struct uv__async { int wfd; }; -struct uv__work { - void (*work)(struct uv__work *w); - void (*done)(struct uv__work *w, int status); - struct uv_loop_s* loop; - void* wq[2]; -}; - #ifndef UV_PLATFORM_SEM_T # define UV_PLATFORM_SEM_T sem_t #endif @@ -218,7 +215,7 @@ typedef struct { #define UV_UDP_SEND_PRIVATE_FIELDS \ void* queue[2]; \ - struct sockaddr_in6 addr; \ + struct sockaddr_storage addr; \ unsigned int nbufs; \ uv_buf_t* bufs; \ ssize_t status; \ diff --git a/deps/uv/include/uv-version.h b/deps/uv/include/uv-version.h index 6f8e080a46..d33c8f8bde 100644 --- a/deps/uv/include/uv-version.h +++ b/deps/uv/include/uv-version.h @@ -32,7 +32,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 11 -#define UV_VERSION_PATCH 26 +#define UV_VERSION_PATCH 28 #define UV_VERSION_IS_RELEASE 1 #endif /* UV_VERSION_H */ diff --git a/deps/uv/include/uv-win.h b/deps/uv/include/uv-win.h index 2f24dfe890..136b0b45de 100644 --- a/deps/uv/include/uv-win.h +++ b/deps/uv/include/uv-win.h @@ -30,6 +30,15 @@ typedef intptr_t ssize_t; #endif #include + +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) +typedef struct pollfd { + SOCKET fd; + short events; + short revents; +} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; +#endif + #include #include #include @@ -45,6 +54,7 @@ typedef intptr_t ssize_t; #endif #include "tree.h" +#include "uv-threadpool.h" #define MAX_PIPENAME_LEN 256 @@ -307,7 +317,11 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); /* Counter to keep track of active udp streams */ \ unsigned int active_udp_streams; \ /* Counter to started timer */ \ - uint64_t timer_counter; + uint64_t timer_counter; \ + /* Threadpool */ \ + void* wq[2]; \ + uv_mutex_t wq_mutex; \ + uv_async_t wq_async; #define UV_REQ_TYPE_PRIVATE \ /* TODO: remove the req suffix */ \ @@ -395,7 +409,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); #define UV_TCP_PRIVATE_FIELDS \ SOCKET socket; \ - int bind_error; \ + int delayed_error; \ union { \ struct { uv_tcp_server_fields }; \ struct { uv_tcp_connection_fields }; \ @@ -520,6 +534,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); unsigned int flags; #define UV_GETADDRINFO_PRIVATE_FIELDS \ + struct uv__work work_req; \ uv_getaddrinfo_cb getaddrinfo_cb; \ void* alloc; \ WCHAR* node; \ @@ -529,6 +544,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); int retcode; #define UV_GETNAMEINFO_PRIVATE_FIELDS \ + struct uv__work work_req; \ uv_getnameinfo_cb getnameinfo_cb; \ struct sockaddr_storage storage; \ int flags; \ @@ -547,6 +563,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); volatile char exit_cb_pending; #define UV_FS_PRIVATE_FIELDS \ + struct uv__work work_req; \ int flags; \ DWORD sys_errno_; \ union { \ @@ -572,6 +589,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); }; #define UV_WORK_PRIVATE_FIELDS \ + struct uv__work work_req; #define UV_FS_EVENT_PRIVATE_FIELDS \ struct uv_fs_event_req_s { \ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index ef2f840d76..df6d9549c1 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -258,7 +258,8 @@ UV_EXTERN const char* uv_version_string(void); */ /* - * Returns the default loop. + * Returns the initialized default loop. It may return NULL in case of + * allocation failture. */ UV_EXTERN uv_loop_t* uv_default_loop(void); @@ -276,20 +277,24 @@ UV_EXTERN int uv_loop_close(uv_loop_t* loop); /* * Allocates and initializes a new loop. - * NOTE: This function is DEPRECATED (to be removed after 0.12), users should - * allocate the loop manually and use uv_loop_init instead. + * + * NOTE: + * This function is DEPRECATED (to be removed after 0.12), users should + * allocate the loop manually and use uv_loop_init instead. */ UV_EXTERN uv_loop_t* uv_loop_new(void); /* * Cleans up a loop once it has finished executio and frees its memory. - * NOTE: This function is DEPRECATED (to be removed after 0.12). Users should use - * uv_loop_close and free the memory manually instead. + * + * NOTE: + * This function is DEPRECATED (to be removed after 0.12). Users should use + * uv_loop_close and free the memory manually instead. */ UV_EXTERN void uv_loop_delete(uv_loop_t*); /* - * Returns size of the loop struct, useful for dynamic lookup with FFI + * Returns size of the loop struct, useful for dynamic lookup with FFI. */ UV_EXTERN size_t uv_loop_size(void); @@ -316,10 +321,10 @@ UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode); UV_EXTERN int uv_loop_alive(const uv_loop_t* loop); /* - * This function will stop the event loop by forcing uv_run to end - * as soon as possible, but not sooner than the next loop iteration. - * If this function was called before blocking for i/o, the loop won't - * block for i/o on this iteration. + * This function will stop the event loop by forcing uv_run to end as soon as + * possible, but not sooner than the next loop iteration. + * If this function was called before blocking for i/o, the loop won't block + * for i/o on this iteration. */ UV_EXTERN void uv_stop(uv_loop_t*); @@ -398,12 +403,12 @@ typedef void (*uv_alloc_cb)(uv_handle_t* handle, * `nread` is > 0 if there is data available, 0 if libuv is done reading for * now, or < 0 on error. * - * The callee is responsible for closing the stream when an error happens. - * Trying to read from the stream again is undefined. + * The callee is responsible for closing the stream when an error happens + * by calling uv_close(). Trying to read from the stream again is undefined. * * The callee is responsible for freeing the buffer, libuv does not reuse it. * The buffer may be a null buffer (where buf->base=NULL and buf->len=0) on - * EOF or error. + * error. */ typedef void (*uv_read_cb)(uv_stream_t* stream, ssize_t nread, @@ -505,17 +510,17 @@ struct uv_req_s { }; -/* Platform-specific request types */ +/* Platform-specific request types. */ UV_PRIVATE_REQ_TYPES /* - * uv_shutdown_t is a subclass of uv_req_t + * 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 uninitialized shutdown request - * struct. The cb is called after shutdown is complete. + * 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 uninitialized shutdown request struct. The cb is called + * after shutdown is complete. */ UV_EXTERN int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, @@ -540,20 +545,19 @@ struct uv_shutdown_s { void* handle_queue[2]; \ UV_HANDLE_PRIVATE_FIELDS \ -/* The abstract base class of all handles. */ +/* The abstract base class of all handles. */ struct uv_handle_s { UV_HANDLE_FIELDS }; /* - * Returns size of various handle types, useful for FFI - * bindings to allocate correct memory without copying struct - * definitions + * Returns size of various handle types, useful for FFI bindings to allocate + * correct memory without copying struct definitions. */ UV_EXTERN size_t uv_handle_size(uv_handle_type type); /* - * Returns size of request types, useful for dynamic lookup with FFI + * Returns size of request types, useful for dynamic lookup with FFI. */ UV_EXTERN size_t uv_req_size(uv_req_type type); @@ -566,7 +570,7 @@ UV_EXTERN size_t uv_req_size(uv_req_type type); * by closing it with uv_close(). * * - A uv_pipe_t, uv_tcp_t, uv_udp_t, etc. handle - basically any handle that - * deals with I/O - is active when it is doing something that involves I/O, + * deals with i/o - is active when it is doing something that involves i/o, * like reading, writing, connecting, accepting new connections, etc. * * - A uv_check_t, uv_idle_t, uv_timer_t, etc. handle is active when it has @@ -601,6 +605,7 @@ UV_EXTERN 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. @@ -617,7 +622,7 @@ UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); UV_STREAM_PRIVATE_FIELDS /* - * uv_stream_t is a subclass of uv_handle_t + * uv_stream_t is a subclass of uv_handle_t. * * uv_stream is an abstract class. * @@ -636,16 +641,16 @@ UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); * the connection. Before calling uv_accept use uv_*_init() must be * called on the client. Non-zero return value indicates an error. * - * When the uv_connection_cb is called it is guaranteed that uv_accept will + * When the uv_connection_cb is called it is guaranteed that uv_accept() will * complete successfully the first time. If you attempt to use it more than - * once, it may fail. It is suggested to only call uv_accept once per + * once, it may fail. It is suggested to only call uv_accept() once per * uv_connection_cb call. */ UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client); /* * Read data from an incoming stream. The callback will be made several - * times until there is no more data to read or uv_read_stop is called. + * 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 UV_EOF. * * When nread < 0, the buf parameter might not point to a valid buffer; @@ -704,17 +709,19 @@ UV_EXTERN int uv_write2(uv_write_t* req, uv_write_cb cb); /* - * Same as `uv_write()`, but won't queue write request if it can't be completed + * Same as uv_write(), but won't queue write request if it can't be completed * immediately. + * * Will return either: - * - >= 0: number of bytes written (can be less than the supplied buffer size) - * - < 0: negative error code + * - > 0: number of bytes written (can be less than the supplied buffer size). + * - < 0: negative error code (UV_EAGAIN is returned if no data can be sent + * immediately). */ UV_EXTERN int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs); -/* uv_write_t is a subclass of uv_req_t */ +/* uv_write_t is a subclass of uv_req_t. */ struct uv_write_s { UV_REQ_FIELDS uv_write_cb cb; @@ -755,15 +762,14 @@ UV_EXTERN int uv_stream_set_blocking(uv_stream_t* handle, int blocking); /* * Used to determine whether a stream is closing or closed. * - * N.B. is only valid between the initialization of the handle - * and the arrival of the close callback, and cannot be used - * to validate the handle. + * N.B. is only valid between the initialization of the handle and the arrival + * of the close callback, and cannot be used to validate the handle. */ UV_EXTERN int uv_is_closing(const uv_handle_t* handle); /* - * uv_tcp_t is a subclass of uv_stream_t + * uv_tcp_t is a subclass of uv_stream_t. * * Represents a TCP stream or TCP server. */ @@ -795,15 +801,16 @@ UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle, /* * Enable/disable simultaneous asynchronous accept requests that are * queued by the operating system when listening for new tcp connections. + * * This setting is used to tune a tcp server for the desired performance. - * Having simultaneous accepts can significantly improve the rate of - * accepting connections (which is why it is enabled by default) but - * may lead to uneven load distribution in multi-process setups. + * Having simultaneous accepts can significantly improve the rate of accepting + * connections (which is why it is enabled by default) but may lead to uneven + * load distribution in multi-process setups. */ UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable); enum uv_tcp_flags { - /* Used with uv_tcp_bind, when an IPv6 address is used */ + /* Used with uv_tcp_bind, when an IPv6 address is used. */ UV_TCP_IPV6ONLY = 1 }; @@ -811,11 +818,11 @@ enum uv_tcp_flags { * Bind the handle to an address and port. `addr` should point to an * initialized struct sockaddr_in or struct sockaddr_in6. * - * When the port is already taken, you can expect to see an UV_EADDRINUSE - * error from either uv_tcp_bind(), uv_listen() or uv_tcp_connect(). + * When the port is already taken, you can expect to see an UV_EADDRINUSE error + * from either uv_tcp_bind(), uv_listen() or uv_tcp_connect(). * - * That is, a successful call to uv_tcp_bind() does not guarantee that - * the call to uv_listen() or uv_tcp_connect() will succeed as well. + * That is, a successful call to uv_tcp_bind() does not guarantee that the call + * to uv_listen() or uv_tcp_connect() will succeed as well. */ UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, @@ -829,7 +836,7 @@ UV_EXTERN int uv_tcp_getpeername(const uv_tcp_t* handle, /* * Establish an IPv4 or IPv6 TCP connection. Provide an initialized TCP handle - * and an uninitialized uv_connect_t*. `addr` should point to an initialized + * and an uninitialized uv_connect_t*. `addr` should point to an initialized * struct sockaddr_in or struct sockaddr_in6. * * The callback is made when the connection has been established or when a @@ -840,7 +847,7 @@ UV_EXTERN int uv_tcp_connect(uv_connect_t* req, const struct sockaddr* addr, uv_connect_cb cb); -/* uv_connect_t is a subclass of uv_req_t */ +/* uv_connect_t is a subclass of uv_req_t. */ struct uv_connect_s { UV_REQ_FIELDS uv_connect_cb cb; @@ -861,9 +868,10 @@ enum uv_udp_flags { * remainder was discarded by the OS. Used in uv_udp_recv_cb. */ UV_UDP_PARTIAL = 2, - /* Indicates if SO_REUSEADDR will be set when binding the handle. + /* + * Indicates if SO_REUSEADDR will be set when binding the handle. * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other - * UNIX platforms, it sets the SO_REUSEADDR flag. What that means is that + * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that * multiple threads or processes can bind to the same address without error * (provided they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. @@ -872,8 +880,7 @@ enum uv_udp_flags { }; /* - * Called after uv_udp_send(). status 0 indicates - * success otherwise error. + * Called after uv_udp_send(). status 0 indicates success otherwise error. */ typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); @@ -882,14 +889,20 @@ typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); * * 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. - * < 0 if a transmission error was detected. + * - 0 if there is no more data to read. You may discard or repurpose + * the read buffer. Note that 0 may also mean that an empty datagram + * was received (in this case `addr` is not NULL). + * - < 0 if a transmission error was detected. * buf uv_buf_t with the received data. - * addr struct sockaddr* containing the address of the sender. - * Can be NULL. 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. + * addr struct sockaddr* containing the address of the sender. Can be NULL. + * 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. + * + * NOTE: + * The receive callback will be called with nread == 0 and addr == NULL when + * there is nothing to read, and with nread == 0 and addr != NULL when an empty + * UDP packet is received. */ typedef void (*uv_udp_recv_cb)(uv_udp_t* handle, ssize_t nread, @@ -897,13 +910,23 @@ typedef void (*uv_udp_recv_cb)(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags); -/* uv_udp_t is a subclass of uv_handle_t */ +/* uv_udp_t is a subclass of uv_handle_t. */ struct uv_udp_s { UV_HANDLE_FIELDS + /* read-only */ + /* + * Number of bytes queued for sending. This field strictly shows how much + * information is currently queued. + */ + size_t send_queue_size; + /* + * Number of send requests currently in the queue awaiting to be processed. + */ + size_t send_queue_count; UV_UDP_PRIVATE_FIELDS }; -/* uv_udp_send_t is a subclass of uv_req_t */ +/* uv_udp_send_t is a subclass of uv_req_t. */ struct uv_udp_send_s { UV_REQ_FIELDS uv_udp_t* handle; @@ -921,16 +944,16 @@ UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle); * Opens an existing file descriptor or SOCKET as a udp handle. * * Unix only: - * The only requirement of the sock argument is that it follows the - * datagram contract (works in unconnected mode, supports sendmsg()/recvmsg(), - * etc.). In other words, other datagram-type sockets like raw sockets or - * netlink sockets can also be passed to this function. - * - * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other - * UNIX platforms, it sets the SO_REUSEADDR flag. What that means is that - * multiple threads or processes can bind to the same address without error - * (provided they all set the flag) but only the last one to bind will receive - * any traffic, in effect "stealing" the port from the previous listener. + * The only requirement of the sock argument is that it follows the datagram + * contract (works in unconnected mode, supports sendmsg()/recvmsg(), etc). + * In other words, other datagram-type sockets like raw sockets or netlink + * sockets can also be passed to this function. + * + * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other Unix + * platforms, it sets the SO_REUSEADDR flag. What that means is that multiple + * threads or processes can bind to the same address without error (provided + * they all set the flag) but only the last one to bind will receive any + * traffic, in effect "stealing" the port from the previous listener. * This behavior is something of an anomaly and may be replaced by an explicit * opt-in mechanism in future versions of libuv. */ @@ -940,7 +963,7 @@ UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); * Bind to an IP address and port. * * Arguments: - * handle UDP handle. Should have been initialized with `uv_udp_init`. + * handle UDP handle. Should have been initialized with uv_udp_init(). * addr struct sockaddr_in or struct sockaddr_in6 with the address and * port to bind to. * flags Indicate how the socket will be bound, UV_UDP_IPV6ONLY and @@ -953,7 +976,7 @@ UV_EXTERN int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags); -UV_EXTERN int uv_udp_getsockname(uv_udp_t* handle, +UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen); @@ -962,10 +985,10 @@ UV_EXTERN int uv_udp_getsockname(uv_udp_t* handle, * * Arguments: * handle UDP handle. Should have been initialized with - * `uv_udp_init`. - * multicast_addr multicast address to set membership for - * interface_addr interface address - * membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP + * uv_udp_init(). + * multicast_addr multicast address to set membership for. + * interface_addr interface address. + * membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP. * * Returns: * 0 on success, or an error code < 0 on failure. @@ -981,8 +1004,8 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle, * * Arguments: * handle UDP handle. Should have been initialized with - * `uv_udp_init`. - * on 1 for on, 0 for off + * uv_udp_init(). + * on 1 for on, 0 for off. * * Returns: * 0 on success, or an error code < 0 on failure. @@ -990,12 +1013,12 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle, UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on); /* - * Set the multicast ttl + * Set the multicast ttl. * * Arguments: * handle UDP handle. Should have been initialized with - * `uv_udp_init`. - * ttl 1 through 255 + * uv_udp_init(). + * ttl 1 through 255. * * Returns: * 0 on success, or an error code < 0 on failure. @@ -1004,12 +1027,12 @@ UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl); /* - * Set the multicast interface to send on + * Set the multicast interface to send on. * * Arguments: * handle UDP handle. Should have been initialized with - * `uv_udp_init`. - * interface_addr interface address + * uv_udp_init(). + * interface_addr interface address. * * Returns: * 0 on success, or an error code < 0 on failure. @@ -1018,12 +1041,12 @@ UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr); /* - * Set broadcast on or off + * Set broadcast on or off. * * Arguments: * handle UDP handle. Should have been initialized with - * `uv_udp_init`. - * on 1 for on, 0 for off + * uv_udp_init(). + * on 1 for on, 0 for off. * * Returns: * 0 on success, or an error code < 0 on failure. @@ -1031,12 +1054,12 @@ UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle, UV_EXTERN int uv_udp_set_broadcast(uv_udp_t* handle, int on); /* - * Set the time to live + * Set the time to live. * * Arguments: * handle UDP handle. Should have been initialized with - * `uv_udp_init`. - * ttl 1 through 255 + * uv_udp_init(). + * ttl 1 through 255. * * Returns: * 0 on success, or an error code < 0 on failure. @@ -1044,13 +1067,12 @@ UV_EXTERN int uv_udp_set_broadcast(uv_udp_t* handle, int on); UV_EXTERN int uv_udp_set_ttl(uv_udp_t* handle, int ttl); /* - * Send data. If the socket has not previously been bound with `uv_udp_bind,` - * it is bound to 0.0.0.0 (the "all interfaces" address) and a random - * port number. + * Send data. If the socket has not previously been bound with uv_udp_bind() 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`. + * handle UDP handle. Should have been initialized with uv_udp_init(). * bufs List of buffers to send. * nbufs Number of buffers in `bufs`. * addr struct sockaddr_in or struct sockaddr_in6 with the address and @@ -1068,12 +1090,25 @@ UV_EXTERN int uv_udp_send(uv_udp_send_t* req, uv_udp_send_cb send_cb); /* - * Receive data. If the socket has not previously been bound with `uv_udp_bind` - * it is bound to 0.0.0.0 (the "all interfaces" address) and a random - * port number. + * Same as uv_udp_send(), but won't queue a send request if it can't be completed + * immediately. + * + * Will return either: + * - >= 0: number of bytes sent (it matches the given buffer size). + * - < 0: negative error code (UV_EAGAIN is returned when the message can't be + * sent immediately). + */ +UV_EXTERN int uv_udp_try_send(uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr); +/* + * Receive data. If the socket has not previously been bound with uv_udp_bind() + * 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`. + * 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. * @@ -1088,7 +1123,7 @@ UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, * Stop listening for incoming datagrams. * * Arguments: - * handle UDP handle. Should have been initialized with `uv_udp_init`. + * handle UDP handle. Should have been initialized with uv_udp_init(). * * Returns: * 0 on success, or an error code < 0 on failure. @@ -1097,7 +1132,7 @@ UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle); /* - * uv_tty_t is a subclass of uv_stream_t + * uv_tty_t is a subclass of uv_stream_t. * * Representing a stream for the console. */ @@ -1109,12 +1144,12 @@ struct uv_tty_s { /* * Initialize a new TTY stream with the given file descriptor. Usually the - * file descriptor will be + * file descriptor will be: * 0 = stdin * 1 = stdout * 2 = stderr * The last argument, readable, specifies if you plan on calling - * uv_read_start with this stream. stdin is readable, stdout is not. + * uv_read_start() with this stream. stdin is readable, stdout is not. * * TTY streams which are not readable have blocking writes. */ @@ -1129,7 +1164,7 @@ UV_EXTERN int uv_tty_set_mode(uv_tty_t*, int mode); * To be called when the program exits. Resets TTY settings to default * values for the next process to take over. * - * This function is async signal-safe on UNIX platforms but can fail with error + * This function is async signal-safe on Unix platforms but can fail with error * code UV_EBUSY if you call it when execution is inside uv_tty_set_mode(). */ UV_EXTERN int uv_tty_reset_mode(void); @@ -1143,15 +1178,16 @@ UV_EXTERN 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_EXTERN uv_handle_type uv_guess_handle(uv_file file); /* - * uv_pipe_t is a subclass of uv_stream_t + * 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. + * Pipe. On Unix this is a Unix domain socket. */ struct uv_pipe_s { UV_HANDLE_FIELDS @@ -1172,17 +1208,17 @@ UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc); UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file); /* - * Bind the pipe to a file path (UNIX) or a name (Windows.) + * Bind the pipe to a file path (Unix) or a name (Windows). * - * Paths on UNIX get truncated to `sizeof(sockaddr_un.sun_path)` bytes, + * Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes, * typically between 92 and 108 bytes. */ UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name); /* - * Connect to the UNIX domain socket or the named pipe. + * Connect to the Unix domain socket or the named pipe. * - * Paths on UNIX get truncated to `sizeof(sockaddr_un.sun_path)` bytes, + * Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes, * typically between 92 and 108 bytes. */ UV_EXTERN void uv_pipe_connect(uv_connect_t* req, @@ -1191,12 +1227,12 @@ UV_EXTERN void uv_pipe_connect(uv_connect_t* req, uv_connect_cb cb); /* - * Get the name of the UNIX domain socket or the named pipe. + * Get the name of the Unix domain socket or the named pipe. * - * A preallocated buffer must be provided. The len parameter holds the - * length of the buffer and it's set to the number of bytes written to the - * buffer on output. If the buffer is not big enough UV_ENOBUFS will be - * returned and len will contain the required size. + * A preallocated buffer must be provided. The len parameter holds the length + * of the buffer and it's set to the number of bytes written to the buffer on + * output. If the buffer is not big enough UV_ENOBUFS will be returned and len + * will contain the required size. */ UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle, char* buf, @@ -1204,17 +1240,18 @@ UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle, /* * This setting applies to Windows only. - * Set the number of pending pipe instance handles when the pipe server - * is waiting for connections. + * + * Set the number of pending pipe instance handles when the pipe server is + * waiting for connections. */ UV_EXTERN void uv_pipe_pending_instances(uv_pipe_t* handle, int count); /* * Used to receive handles over ipc pipes. * - * First - call `uv_pipe_pending_count`, if it is > 0 - initialize handle - * using type, returned by `uv_pipe_pending_type` and call - * `uv_accept(pipe, handle)`. + * First - call uv_pipe_pending_count(), if it is > 0 - initialize handle + * using type, returned by uv_pipe_pending_type() and call + * uv_accept(pipe, handle). */ UV_EXTERN int uv_pipe_pending_count(uv_pipe_t* handle); UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); @@ -1227,10 +1264,10 @@ UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); * * The purpose of uv_poll is to enable integrating external libraries that * rely on the event loop to signal it about the socket status changes, like - * c-ares or libssh2. Using uv_poll_t for any other other purpose is not - * recommended; uv_tcp_t, uv_udp_t, etc. provide an implementation that is - * much faster and more scalable than what can be achieved with uv_poll_t, - * especially on Windows. + * c-ares or libssh2. Using uv_poll_t for any other purpose is not recommended; + * uv_tcp_t, uv_udp_t, etc. provide an implementation that is much faster and + * more scalable than what can be achieved with uv_poll_t, especially on + * Windows. * * It is possible that uv_poll occasionally signals that a file descriptor is * readable or writable even when it isn't. The user should therefore always @@ -1245,7 +1282,7 @@ UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); * but it might also start polling another socket. However the fd can be safely * closed immediately after a call to uv_poll_stop() or uv_close(). * - * On windows only sockets can be polled with uv_poll. On unix any file + * On windows only sockets can be polled with uv_poll. On Unix any file * descriptor that would be accepted by poll(2) can be used with uv_poll. */ struct uv_poll_s { @@ -1262,8 +1299,10 @@ enum uv_poll_event { /* Initialize the poll watcher using a file descriptor. */ UV_EXTERN int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd); -/* Initialize the poll watcher using a socket descriptor. On unix this is */ -/* identical to uv_poll_init. On windows it takes a SOCKET handle. */ +/* + * Initialize the poll watcher using a socket descriptor. On Unix this is + * identical to uv_poll_init. On windows it takes a SOCKET handle. + */ UV_EXTERN int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t socket); @@ -1347,11 +1386,15 @@ UV_EXTERN int uv_idle_stop(uv_idle_t* idle); /* * uv_async_t is a subclass of uv_handle_t. * - * 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 after the call to async_send. Unlike all other - * libuv functions, uv_async_send can be called from another thread. + * uv_async_send() wakes up the event loop and calls the async handle's callback. + * + * Unlike all other libuv functions, uv_async_send() can be called from another + * thread. + * + * NOTE: + * 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 after the call to async_send. */ struct uv_async_s { UV_HANDLE_FIELDS @@ -1420,7 +1463,7 @@ UV_EXTERN uint64_t uv_timer_get_repeat(const uv_timer_t* handle); /* - * uv_getaddrinfo_t is a subclass of uv_req_t + * uv_getaddrinfo_t is a subclass of uv_req_t. * * Request object for uv_getaddrinfo. */ @@ -1464,7 +1507,7 @@ UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai); /* -* uv_getnameinfo_t is a subclass of uv_req_t +* uv_getnameinfo_t is a subclass of uv_req_t. * * Request object for uv_getnameinfo. */ @@ -1490,14 +1533,15 @@ UV_EXTERN int uv_getnameinfo(uv_loop_t* loop, int flags); -/* uv_spawn() options */ +/* uv_spawn() options. */ typedef enum { UV_IGNORE = 0x00, UV_CREATE_PIPE = 0x01, UV_INHERIT_FD = 0x02, UV_INHERIT_STREAM = 0x04, - /* When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE + /* + * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE * determine the direction of flow, from the child process' perspective. Both * flags may be specified to create a duplex data stream. */ @@ -1578,7 +1622,7 @@ enum uv_process_flags { /* * Do not wrap any arguments in quotes, or perform any other escaping, when * converting the argument list into a command line string. This option is - * only meaningful on Windows systems. On unix it is silently ignored. + * only meaningful on Windows systems. On Unix it is silently ignored. */ UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2), /* @@ -1591,14 +1635,14 @@ enum uv_process_flags { UV_PROCESS_DETACHED = (1 << 3), /* * Hide the subprocess console window that would normally be created. This - * option is only meaningful on Windows systems. On unix it is silently + * option is only meaningful on Windows systems. On Unix it is silently * ignored. */ UV_PROCESS_WINDOWS_HIDE = (1 << 4) }; /* - * uv_process_t is a subclass of uv_handle_t + * uv_process_t is a subclass of uv_handle_t. */ struct uv_process_s { UV_HANDLE_FIELDS @@ -1625,7 +1669,7 @@ UV_EXTERN int uv_spawn(uv_loop_t* loop, /* * Kills the process with the specified signal. The user must still - * call uv_close on the process. + * call uv_close() on the process. * * Emulates some aspects of Unix exit status on Windows, in that while the * underlying process will be terminated with a status of `1`, @@ -1651,7 +1695,7 @@ UV_EXTERN int uv_kill(int pid, int signum); /* - * uv_work_t is a subclass of uv_req_t + * uv_work_t is a subclass of uv_req_t. */ struct uv_work_s { UV_REQ_FIELDS @@ -1686,7 +1730,7 @@ UV_EXTERN int uv_queue_work(uv_loop_t* loop, * - A uv_work_t or uv_getaddrinfo_t request has its callback invoked with * status == UV_ECANCELED. * - * This function is currently only implemented on UNIX platforms. On Windows, + * This function is currently only implemented on Unix platforms. On Windows, * it always returns UV_ENOSYS. */ UV_EXTERN int uv_cancel(uv_req_t* req); @@ -1755,15 +1799,15 @@ typedef struct { UV_EXTERN int uv_getrusage(uv_rusage_t* rusage); /* - * This allocates cpu_infos array, and sets count. The array - * is freed using uv_free_cpu_info(). + * This allocates cpu_infos array, and sets count. The array is freed + * using uv_free_cpu_info(). */ UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); /* - * This allocates addresses array, and sets count. The array - * is freed using uv_free_interface_addresses(). + * This allocates addresses array, and sets count. The array is freed + * using uv_free_interface_addresses(). */ UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses, int* count); @@ -1805,6 +1849,7 @@ typedef enum { UV_FS_UNLINK, UV_FS_RMDIR, UV_FS_MKDIR, + UV_FS_MKDTEMP, UV_FS_RENAME, UV_FS_READDIR, UV_FS_LINK, @@ -1814,7 +1859,7 @@ typedef enum { UV_FS_FCHOWN } uv_fs_type; -/* uv_fs_t is a subclass of uv_req_t */ +/* uv_fs_t is a subclass of uv_req_t. */ struct uv_fs_s { UV_REQ_FIELDS uv_fs_type fs_type; @@ -1823,7 +1868,7 @@ struct uv_fs_s { ssize_t result; void* ptr; const char* path; - uv_stat_t statbuf; /* Stores the result of uv_fs_stat and uv_fs_fstat. */ + uv_stat_t statbuf; /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */ UV_FS_PRIVATE_FIELDS }; @@ -1847,6 +1892,9 @@ UV_EXTERN int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); +UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, + uv_fs_cb cb); + UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); @@ -1890,14 +1938,14 @@ UV_EXTERN 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. + * 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 /* - * This flag can be used with uv_fs_symlink on Windows - * to specify whether the symlink is to be created using junction points. + * This flag can be used with uv_fs_symlink() on Windows to specify whether + * the symlink is to be created using junction points. */ #define UV_FS_SYMLINK_JUNCTION 0x0002 @@ -1975,7 +2023,7 @@ UV_EXTERN int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buf, size_t* len); /* - * UNIX signal handling on a per-event loop basis. The implementation is not + * Unix signal handling on a per-event loop basis. The implementation is not * ultra efficient so don't go creating a million event loops with a million * signal watchers. * @@ -2030,14 +2078,16 @@ UV_EXTERN int uv_signal_stop(uv_signal_t* handle); /* * Gets load average. + * * See: http://en.wikipedia.org/wiki/Load_(computing) + * * Returns [0,0,0] on Windows. */ UV_EXTERN void uv_loadavg(double avg[3]); /* - * Flags to be passed to uv_fs_event_start. + * Flags to be passed to uv_fs_event_start(). */ enum uv_fs_event_flags { /* @@ -2088,32 +2138,34 @@ UV_EXTERN int uv_fs_event_getpath(uv_fs_event_t* handle, size_t* len); -/* Utility */ +/* Utilities. */ -/* Convert string ip addresses to binary structures */ +/* Convert string ip addresses to binary structures. */ UV_EXTERN int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr); UV_EXTERN int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr); -/* Convert binary addresses to strings */ +/* Convert binary addresses to strings. */ UV_EXTERN int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size); UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size); -/* Cross-platform IPv6-capable implementation of the 'standard' inet_ntop */ -/* and inet_pton functions. On success they return 0. If an error */ -/* the target of the `dst` pointer is unmodified. */ +/* + * Cross-platform IPv6-capable implementation of the 'standard' inet_ntop() and + * inet_pton() functions. On success they return 0. If an error the target of + * the `dst` pointer is unmodified. + */ UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size); UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst); -/* Gets the executable path */ +/* Gets the executable path. */ UV_EXTERN int uv_exepath(char* buffer, size_t* size); -/* Gets the current working directory */ +/* Gets the current working directory. */ UV_EXTERN int uv_cwd(char* buffer, size_t* size); -/* Changes the current working directory */ +/* Changes the current working directory. */ UV_EXTERN int uv_chdir(const char* dir); -/* Gets memory info in bytes */ +/* Gets memory info in bytes. */ UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); @@ -2139,13 +2191,13 @@ UV_EXTERN extern uint64_t uv_hrtime(void); * * Note that this function works on a best-effort basis: there is no guarantee * that libuv can discover all file descriptors that were inherited. In general - * it does a better job on Windows than it does on unix. + * it does a better job on Windows than it does on Unix. */ UV_EXTERN void uv_disable_stdio_inheritance(void); /* * Opens a shared library. The filename is in utf-8. Returns 0 on success and - * -1 on error. Call `uv_dlerror(uv_lib_t*)` to get the error message. + * -1 on error. Call uv_dlerror(uv_lib_t*) to get the error message. */ UV_EXTERN int uv_dlopen(const char* filename, uv_lib_t* lib); @@ -2166,8 +2218,8 @@ UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr); UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib); /* - * The mutex functions return 0 on success or an error code < 0 - * (unless the return type is void, of course). + * The mutex functions return 0 on success or an error code < 0 (unless the + * return type is void, of course). */ UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); @@ -2216,31 +2268,35 @@ UV_EXTERN int uv_barrier_init(uv_barrier_t* barrier, unsigned int count); UV_EXTERN void uv_barrier_destroy(uv_barrier_t* barrier); UV_EXTERN int uv_barrier_wait(uv_barrier_t* barrier); -/* Waits on a condition variable without a timeout. +/* + * Waits on a condition variable without a timeout. * - * Note: - * 1. callers should be prepared to deal with spurious wakeups. + * NOTE: + * 1. callers should be prepared to deal with spurious wakeups. */ UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex); -/* Waits on a condition variable with a timeout in nano seconds. +/* + * Waits on a condition variable with a timeout in nano seconds. * Returns 0 for success or UV_ETIMEDOUT on timeout, It aborts when other * errors happen. * - * Note: - * 1. callers should be prepared to deal with spurious wakeups. - * 2. the granularity of timeout on Windows is never less than one millisecond. - * 3. uv_cond_timedwait takes a relative timeout, not an absolute time. + * NOTE: + * 1. callers should be prepared to deal with spurious wakeups. + * 2. the granularity of timeout on Windows is never less than one millisecond. + * 3. uv_cond_timedwait() takes a relative timeout, not an absolute time. */ UV_EXTERN int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout); -/* Runs a function once and only once. Concurrent calls to uv_once() with the +/* + * Runs a function once and only once. Concurrent calls to uv_once() with the * same guard will block all callers except one (it's unspecified which one). * The guard should be initialized statically with the UV_ONCE_INIT macro. */ UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)); -/* Thread-local storage. These functions largely follow the semantics of +/* + * Thread-local storage. These functions largely follow the semantics of * pthread_key_create(), pthread_key_delete(), pthread_getspecific() and * pthread_setspecific(). * @@ -2278,11 +2334,11 @@ union uv_any_req { struct uv_loop_s { /* User data - use this for whatever. */ void* data; - /* Loop reference counting */ + /* Loop reference counting. */ unsigned int active_handles; void* handle_queue[2]; void* active_reqs[2]; - /* Internal flag to signal loop stop */ + /* Internal flag to signal loop stop. */ unsigned int stop_flag; UV_LOOP_PRIVATE_FIELDS }; diff --git a/deps/uv/src/threadpool.c b/deps/uv/src/threadpool.c new file mode 100644 index 0000000000..33890f02b5 --- /dev/null +++ b/deps/uv/src/threadpool.c @@ -0,0 +1,303 @@ +/* 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-common.h" + +#if !defined(_WIN32) +# include "unix/internal.h" +#else +# include "win/req-inl.h" +/* TODO(saghul): unify internal req functions */ +static void uv__req_init(uv_loop_t* loop, + uv_req_t* req, + uv_req_type type) { + uv_req_init(loop, req); + req->type = type; + uv__req_register(loop, req); +} +# define uv__req_init(loop, req, type) \ + uv__req_init((loop), (uv_req_t*)(req), (type)) +#endif + +#include + +#define MAX_THREADPOOL_SIZE 128 + +static uv_once_t once = UV_ONCE_INIT; +static uv_cond_t cond; +static uv_mutex_t mutex; +static unsigned int nthreads; +static uv_thread_t* threads; +static uv_thread_t default_threads[4]; +static QUEUE exit_message; +static QUEUE wq; +static volatile int initialized; + + +static void uv__cancelled(struct uv__work* w) { + abort(); +} + + +/* To avoid deadlock with uv_cancel() it's crucial that the worker + * never holds the global mutex and the loop-local mutex at the same time. + */ +static void worker(void* arg) { + struct uv__work* w; + QUEUE* q; + + (void) arg; + + for (;;) { + uv_mutex_lock(&mutex); + + while (QUEUE_EMPTY(&wq)) + uv_cond_wait(&cond, &mutex); + + q = QUEUE_HEAD(&wq); + + if (q == &exit_message) + uv_cond_signal(&cond); + else { + QUEUE_REMOVE(q); + QUEUE_INIT(q); /* Signal uv_cancel() that the work req is + executing. */ + } + + uv_mutex_unlock(&mutex); + + if (q == &exit_message) + break; + + w = QUEUE_DATA(q, struct uv__work, wq); + w->work(w); + + uv_mutex_lock(&w->loop->wq_mutex); + w->work = NULL; /* Signal uv_cancel() that the work req is done + executing. */ + QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq); + uv_async_send(&w->loop->wq_async); + uv_mutex_unlock(&w->loop->wq_mutex); + } +} + + +static void post(QUEUE* q) { + uv_mutex_lock(&mutex); + QUEUE_INSERT_TAIL(&wq, q); + uv_cond_signal(&cond); + uv_mutex_unlock(&mutex); +} + + +#ifndef _WIN32 +UV_DESTRUCTOR(static void cleanup(void)) { + unsigned int i; + + if (initialized == 0) + return; + + post(&exit_message); + + for (i = 0; i < nthreads; i++) + if (uv_thread_join(threads + i)) + abort(); + + if (threads != default_threads) + free(threads); + + uv_mutex_destroy(&mutex); + uv_cond_destroy(&cond); + + threads = NULL; + nthreads = 0; + initialized = 0; +} +#endif + + +static void init_once(void) { + unsigned int i; + const char* val; + + nthreads = ARRAY_SIZE(default_threads); + val = getenv("UV_THREADPOOL_SIZE"); + if (val != NULL) + nthreads = atoi(val); + if (nthreads == 0) + nthreads = 1; + if (nthreads > MAX_THREADPOOL_SIZE) + nthreads = MAX_THREADPOOL_SIZE; + + threads = default_threads; + if (nthreads > ARRAY_SIZE(default_threads)) { + threads = malloc(nthreads * sizeof(threads[0])); + if (threads == NULL) { + nthreads = ARRAY_SIZE(default_threads); + threads = default_threads; + } + } + + if (uv_cond_init(&cond)) + abort(); + + if (uv_mutex_init(&mutex)) + abort(); + + QUEUE_INIT(&wq); + + for (i = 0; i < nthreads; i++) + if (uv_thread_create(threads + i, worker, NULL)) + abort(); + + initialized = 1; +} + + +void uv__work_submit(uv_loop_t* loop, + struct uv__work* w, + void (*work)(struct uv__work* w), + void (*done)(struct uv__work* w, int status)) { + uv_once(&once, init_once); + w->loop = loop; + w->work = work; + w->done = done; + post(&w->wq); +} + + +static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { + int cancelled; + + uv_mutex_lock(&mutex); + uv_mutex_lock(&w->loop->wq_mutex); + + cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL; + if (cancelled) + QUEUE_REMOVE(&w->wq); + + uv_mutex_unlock(&w->loop->wq_mutex); + uv_mutex_unlock(&mutex); + + if (!cancelled) + return UV_EBUSY; + + w->work = uv__cancelled; + uv_mutex_lock(&loop->wq_mutex); + QUEUE_INSERT_TAIL(&loop->wq, &w->wq); + uv_async_send(&loop->wq_async); + uv_mutex_unlock(&loop->wq_mutex); + + return 0; +} + + +void uv__work_done(uv_async_t* handle) { + struct uv__work* w; + uv_loop_t* loop; + QUEUE* q; + QUEUE wq; + int err; + + loop = container_of(handle, uv_loop_t, wq_async); + QUEUE_INIT(&wq); + + uv_mutex_lock(&loop->wq_mutex); + if (!QUEUE_EMPTY(&loop->wq)) { + q = QUEUE_HEAD(&loop->wq); + QUEUE_SPLIT(&loop->wq, q, &wq); + } + uv_mutex_unlock(&loop->wq_mutex); + + while (!QUEUE_EMPTY(&wq)) { + q = QUEUE_HEAD(&wq); + QUEUE_REMOVE(q); + + w = container_of(q, struct uv__work, wq); + err = (w->work == uv__cancelled) ? UV_ECANCELED : 0; + w->done(w, err); + } +} + + +static void uv__queue_work(struct uv__work* w) { + uv_work_t* req = container_of(w, uv_work_t, work_req); + + req->work_cb(req); +} + + +static void uv__queue_done(struct uv__work* w, int err) { + uv_work_t* req; + + req = container_of(w, uv_work_t, work_req); + uv__req_unregister(req->loop, req); + + if (req->after_work_cb == NULL) + return; + + req->after_work_cb(req, err); +} + + +int uv_queue_work(uv_loop_t* loop, + uv_work_t* req, + uv_work_cb work_cb, + uv_after_work_cb after_work_cb) { + if (work_cb == NULL) + return UV_EINVAL; + + uv__req_init(loop, req, UV_WORK); + req->loop = loop; + req->work_cb = work_cb; + req->after_work_cb = after_work_cb; + uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done); + return 0; +} + + +int uv_cancel(uv_req_t* req) { + struct uv__work* wreq; + uv_loop_t* loop; + + switch (req->type) { + case UV_FS: + loop = ((uv_fs_t*) req)->loop; + wreq = &((uv_fs_t*) req)->work_req; + break; + case UV_GETADDRINFO: + loop = ((uv_getaddrinfo_t*) req)->loop; + wreq = &((uv_getaddrinfo_t*) req)->work_req; + break; + case UV_GETNAMEINFO: + loop = ((uv_getnameinfo_t*) req)->loop; + wreq = &((uv_getnameinfo_t*) req)->work_req; + break; + case UV_WORK: + loop = ((uv_work_t*) req)->loop; + wreq = &((uv_work_t*) req)->work_req; + break; + default: + return UV_EINVAL; + } + + return uv__work_cancel(loop, req, wreq); +} diff --git a/deps/uv/src/unix/aix.c b/deps/uv/src/unix/aix.c index 2521681305..eb90111345 100644 --- a/deps/uv/src/unix/aix.c +++ b/deps/uv/src/unix/aix.c @@ -39,12 +39,235 @@ #include #include #include +#include #include #include #include #include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define RDWR_BUF_SIZE 4096 +#define EQ(a,b) (strcmp(a,b) == 0) + +int uv__platform_loop_init(uv_loop_t* loop, int default_loop) { + loop->fs_fd = -1; + + /* Passing maxfd of -1 should mean the limit is determined + * by the user's ulimit or the global limit as per the doc */ + loop->backend_fd = pollset_create(-1); + + if (loop->backend_fd == -1) + return -1; + + return 0; +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + if (loop->fs_fd != -1) { + uv__close(loop->fs_fd); + loop->fs_fd = -1; + } + + if (loop->backend_fd != -1) { + pollset_destroy(loop->backend_fd); + loop->backend_fd = -1; + } +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + struct pollfd events[1024]; + struct pollfd pqry; + struct pollfd* pe; + struct poll_ctl pc; + QUEUE* q; + uv__io_t* w; + uint64_t base; + uint64_t diff; + int nevents; + int count; + int nfds; + int i; + int rc; + int add_failed; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + assert(w->fd >= 0); + assert(w->fd < (int) loop->nwatchers); + + pc.events = w->pevents; + pc.fd = w->fd; + + add_failed = 0; + if (w->events == 0) { + pc.cmd = PS_ADD; + if (pollset_ctl(loop->backend_fd, &pc, 1)) { + if (errno != EINVAL) { + assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); + abort(); + } + /* Check if the fd is already in the pollset */ + pqry.fd = pc.fd; + rc = pollset_query(loop->backend_fd, &pqry); + switch (rc) { + case -1: + assert(0 && "Failed to query pollset for file descriptor"); + abort(); + case 0: + assert(0 && "Pollset does not contain file descriptor"); + abort(); + } + /* If we got here then the pollset already contained the file descriptor even though + * we didn't think it should. This probably shouldn't happen, but we can continue. */ + add_failed = 1; + } + } + if (w->events != 0 || add_failed) { + /* Modify, potentially removing events -- need to delete then add. + * Could maybe mod if we knew for sure no events are removed, but + * content of w->events is handled above as not reliable (falls back) + * so may require a pollset_query() which would have to be pretty cheap + * compared to a PS_DELETE to be worth optimising. Alternatively, could + * lazily remove events, squelching them in the mean time. */ + pc.cmd = PS_DELETE; + if (pollset_ctl(loop->backend_fd, &pc, 1)) { + assert(0 && "Failed to delete file descriptor (pc.fd) from pollset"); + abort(); + } + pc.cmd = PS_ADD; + if (pollset_ctl(loop->backend_fd, &pc, 1)) { + assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); + abort(); + } + } + + w->events = w->pevents; + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + + for (;;) { + nfds = pollset_poll(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == 0) { + assert(timeout != -1); + return; + } + + if (nfds == -1) { + if (errno != EINTR) { + abort(); + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + nevents = 0; + + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + + for (i = 0; i < nfds; i++) { + pe = events + i; + pc.cmd = PS_DELETE; + pc.fd = pe->fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (pc.fd == -1) + continue; + + assert(pc.fd >= 0); + assert((unsigned) pc.fd < loop->nwatchers); + + w = loop->watchers[pc.fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + */ + pollset_ctl(loop->backend_fd, &pc, 1); + continue; + } + + w->cb(loop, w, pe->revents); + nevents++; + } + + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + diff = loop->time - base; + if (diff >= (uint64_t) timeout) + return; + + timeout -= diff; + } +} + + uint64_t uv__hrtime(uv_clocktype_t type) { uint64_t G = 1000000000; timebasestruct_t t; @@ -58,28 +281,24 @@ uint64_t uv__hrtime(uv_clocktype_t type) { * We could use a static buffer for the path manipulations that we need outside * of the function, but this function could be called by multiple consumers and * we don't want to potentially create a race condition in the use of snprintf. + * There is no direct way of getting the exe path in AIX - either through /procfs + * or through some libc APIs. The below approach is to parse the argv[0]'s pattern + * and use it in conjunction with PATH environment variable to craft one. */ int uv_exepath(char* buffer, size_t* size) { ssize_t res; - char pp[64], cwdl[PATH_MAX]; + char cwd[PATH_MAX], cwdl[PATH_MAX]; + char symlink[PATH_MAX], temp_buffer[PATH_MAX]; + char pp[64]; struct psinfo ps; int fd; + char **argv; - if (buffer == NULL) - return (-1); - - if (size == NULL) - return (-1); - - (void) snprintf(pp, sizeof(pp), "/proc/%lu/cwd", (unsigned long) getpid()); - - res = readlink(pp, cwdl, sizeof(cwdl) - 1); - if (res < 0) - return res; + if ((buffer == NULL) || (size == NULL)) + return -EINVAL; - cwdl[res] = '\0'; + snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid()); - (void) snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid()); fd = open(pp, O_RDONLY); if (fd < 0) return fd; @@ -89,9 +308,163 @@ int uv_exepath(char* buffer, size_t* size) { if (res < 0) return res; - (void) snprintf(buffer, *size, "%s%s", cwdl, ps.pr_fname); - *size = strlen(buffer); - return 0; + if (ps.pr_argv == 0) + return -EINVAL; + + argv = (char **) *((char ***) (intptr_t) ps.pr_argv); + + if ((argv == NULL) || (argv[0] == NULL)) + return -EINVAL; + + /* + * Three possibilities for argv[0]: + * i) an absolute path such as: /home/user/myprojects/nodejs/node + * ii) a relative path such as: ./node or ./myprojects/nodejs/node + * iii) a bare filename such as "node", after exporting PATH variable + * to its location. + */ + + /* case #1, absolute path. */ + if (argv[0][0] == '/') { + snprintf(symlink, PATH_MAX-1, "%s", argv[0]); + + /* This could or could not be a symlink. */ + res = readlink(symlink, temp_buffer, PATH_MAX-1); + + /* if readlink fails, it is a normal file just copy symlink to the + * outbut buffer. + */ + if (res < 0) { + assert(*size > strlen(symlink)); + strcpy(buffer, symlink); + + /* If it is a link, the resolved filename is again a relative path, + * make it absolute. + */ + } else { + assert(*size > (strlen(symlink) + 1 + strlen(temp_buffer))); + snprintf(buffer, *size-1, "%s/%s", dirname(symlink), temp_buffer); + } + *size = strlen(buffer); + return 0; + + /* case #2, relative path with usage of '.' */ + } else if (argv[0][0] == '.') { + char *relative = strchr(argv[0], '/'); + if (relative == NULL) + return -EINVAL; + + /* Get the current working directory to resolve the relative path. */ + snprintf(cwd, PATH_MAX-1, "/proc/%lu/cwd", (unsigned long) getpid()); + + /* This is always a symlink, resolve it. */ + res = readlink(cwd, cwdl, sizeof(cwdl) - 1); + if (res < 0) + return -errno; + + snprintf(symlink, PATH_MAX-1, "%s%s", cwdl, relative + 1); + + res = readlink(symlink, temp_buffer, PATH_MAX-1); + if (res < 0) { + assert(*size > strlen(symlink)); + strcpy(buffer, symlink); + } else { + assert(*size > (strlen(symlink) + 1 + strlen(temp_buffer))); + snprintf(buffer, *size-1, "%s/%s", dirname(symlink), temp_buffer); + } + *size = strlen(buffer); + return 0; + + /* case #3, relative path without usage of '.', such as invocations in Node test suite. */ + } else if (strchr(argv[0], '/') != NULL) { + /* Get the current working directory to resolve the relative path. */ + snprintf(cwd, PATH_MAX-1, "/proc/%lu/cwd", (unsigned long) getpid()); + + /* This is always a symlink, resolve it. */ + res = readlink(cwd, cwdl, sizeof(cwdl) - 1); + if (res < 0) + return -errno; + + snprintf(symlink, PATH_MAX-1, "%s%s", cwdl, argv[0]); + + res = readlink(symlink, temp_buffer, PATH_MAX-1); + if (res < 0) { + assert(*size > strlen(symlink)); + strcpy(buffer, symlink); + } else { + assert(*size > (strlen(symlink) + 1 + strlen(temp_buffer))); + snprintf(buffer, *size-1, "%s/%s", dirname(symlink), temp_buffer); + } + *size = strlen(buffer); + return 0; + /* Usage of absolute filename with location exported in PATH */ + } else { + char clonedpath[8192]; /* assume 8k buffer will fit PATH */ + char *token = NULL; + struct stat statstruct; + + /* Get the paths. */ + char *path = getenv("PATH"); + if(sizeof(clonedpath) <= strlen(path)) + return -EINVAL; + + /* Get a local copy. */ + strcpy(clonedpath, path); + + /* Tokenize. */ + token = strtok(clonedpath, ":"); + + /* Get current working directory. (may be required in the loop). */ + snprintf(cwd, PATH_MAX-1, "/proc/%lu/cwd", (unsigned long) getpid()); + res = readlink(cwd, cwdl, sizeof(cwdl) - 1); + if (res < 0) + return -errno; + /* Run through the tokens, append our executable file name with each, + * and see which one succeeds. Exit on first match. */ + while(token != NULL) { + if (token[0] == '.') { + /* Path contains a token relative to current directory. */ + char *relative = strchr(token, '/'); + if (relative != NULL) + /* A path which is not current directory. */ + snprintf(symlink, PATH_MAX-1, "%s%s/%s", cwdl, relative+1, ps.pr_fname); + else + snprintf(symlink, PATH_MAX-1, "%s%s", cwdl, ps.pr_fname); + if (stat(symlink, &statstruct) != -1) { + /* File exists. Resolve if it is a link. */ + res = readlink(symlink, temp_buffer, PATH_MAX-1); + if (res < 0) { + assert(*size > strlen(symlink)); + strcpy(buffer, symlink); + } else { + assert(*size > (strlen(symlink) + 1 + strlen(temp_buffer))); + snprintf(buffer, *size-1, "%s/%s", dirname(symlink), temp_buffer); + } + *size = strlen(buffer); + return 0; + } + + /* Absolute path names. */ + } else { + snprintf(symlink, PATH_MAX-1, "%s/%s", token, ps.pr_fname); + if (stat(symlink, &statstruct) != -1) { + res = readlink(symlink, temp_buffer, PATH_MAX-1); + if (res < 0) { + assert(*size > strlen(symlink)); + strcpy(buffer, symlink); + } else { + assert(*size > (strlen(symlink) + 1 + strlen(temp_buffer))); + snprintf(buffer, *size-1, "%s/%s", dirname(symlink), temp_buffer); + } + *size = strlen(buffer); + return 0; + } + } + token = strtok(NULL, ":"); + } + /* Out of tokens (path entries), and no match found */ + return -EINVAL; + } } @@ -128,8 +501,369 @@ void uv_loadavg(double avg[3]) { } +static char *uv__rawname(char *cp) { + static char rawbuf[FILENAME_MAX+1]; + char *dp = rindex(cp, '/'); + + if (dp == 0) + return 0; + + *dp = 0; + strcpy(rawbuf, cp); + *dp = '/'; + strcat(rawbuf, "/r"); + strcat(rawbuf, dp+1); + return rawbuf; +} + + +/* + * Determine whether given pathname is a directory + * Returns 0 if the path is a directory, -1 if not + * + * Note: Opportunity here for more detailed error information but + * that requires changing callers of this function as well + */ +static int uv__path_is_a_directory(char* filename) { + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + return -1; /* failed: not a directory, assume it is a file */ + + if (statbuf.st_type == VDIR) + return 0; + + return -1; +} + + +/* + * Check whether AHAFS is mounted. + * Returns 0 if AHAFS is mounted, or an error code < 0 on failure + */ +static int uv__is_ahafs_mounted(void){ + int rv, i = 2; + struct vmount *p; + int size_multiplier = 10; + size_t siz = sizeof(struct vmount)*size_multiplier; + struct vmount *vmt; + const char *dev = "/aha"; + char *obj, *stub; + + p = malloc(siz); + if (p == NULL) + return -errno; + + /* Retrieve all mounted filesystems */ + rv = mntctl(MCTL_QUERY, siz, (char*)p); + if (rv < 0) + return -errno; + if (rv == 0) { + /* buffer was not large enough, reallocate to correct size */ + siz = *(int*)p; + free(p); + p = malloc(siz); + if (p == NULL) + return -errno; + rv = mntctl(MCTL_QUERY, siz, (char*)p); + if (rv < 0) + return -errno; + } + + /* Look for dev in filesystems mount info */ + for(vmt = p, i = 0; i < rv; i++) { + obj = vmt2dataptr(vmt, VMT_OBJECT); /* device */ + stub = vmt2dataptr(vmt, VMT_STUB); /* mount point */ + + if (EQ(obj, dev) || EQ(uv__rawname(obj), dev) || EQ(stub, dev)) { + free(p); /* Found a match */ + return 0; + } + vmt = (struct vmount *) ((char *) vmt + vmt->vmt_length); + } + + /* /aha is required for monitoring filesystem changes */ + return -1; +} + +/* + * Recursive call to mkdir() to create intermediate folders, if any + * Returns code from mkdir call + */ +static int uv__makedir_p(const char *dir) { + char tmp[256]; + char *p = NULL; + size_t len; + int err; + + snprintf(tmp, sizeof(tmp),"%s",dir); + len = strlen(tmp); + if (tmp[len - 1] == '/') + tmp[len - 1] = 0; + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = 0; + err = mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if(err != 0) + return err; + *p = '/'; + } + } + return mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +} + +/* + * Creates necessary subdirectories in the AIX Event Infrastructure + * file system for monitoring the object specified. + * Returns code from mkdir call + */ +static int uv__make_subdirs_p(const char *filename) { + char cmd[2048]; + char *p; + int rc = 0; + + /* Strip off the monitor file name */ + p = strrchr(filename, '/'); + + if (p == NULL) + return 0; + + if (uv__path_is_a_directory((char*)filename) == 0) { + sprintf(cmd, "/aha/fs/modDir.monFactory"); + } else { + sprintf(cmd, "/aha/fs/modFile.monFactory"); + } + + strncat(cmd, filename, (p - filename)); + rc = uv__makedir_p(cmd); + + if (rc == -1 && errno != EEXIST){ + return -errno; + } + + return rc; +} + + +/* + * Checks if /aha is mounted, then proceeds to set up the monitoring + * objects for the specified file. + * Returns 0 on success, or an error code < 0 on failure + */ +static int uv__setup_ahafs(const char* filename, int *fd) { + int rc = 0; + char mon_file_write_string[RDWR_BUF_SIZE]; + char mon_file[PATH_MAX]; + int file_is_directory = 0; /* -1 == NO, 0 == YES */ + + /* Create monitor file name for object */ + file_is_directory = uv__path_is_a_directory((char*)filename); + + if (file_is_directory == 0) + sprintf(mon_file, "/aha/fs/modDir.monFactory"); + else + sprintf(mon_file, "/aha/fs/modFile.monFactory"); + + if ((strlen(mon_file) + strlen(filename) + 5) > PATH_MAX) + return -ENAMETOOLONG; + + /* Make the necessary subdirectories for the monitor file */ + rc = uv__make_subdirs_p(filename); + if (rc == -1 && errno != EEXIST) + return rc; + + strcat(mon_file, filename); + strcat(mon_file, ".mon"); + + *fd = 0; errno = 0; + + /* Open the monitor file, creating it if necessary */ + *fd = open(mon_file, O_CREAT|O_RDWR); + if (*fd < 0) + return -errno; + + /* Write out the monitoring specifications. + * In this case, we are monitoring for a state change event type + * CHANGED=YES + * We will be waiting in select call, rather than a read: + * WAIT_TYPE=WAIT_IN_SELECT + * We only want minimal information for files: + * INFO_LVL=1 + * For directories, we want more information to track what file + * caused the change + * INFO_LVL=2 + */ + + if (file_is_directory == 0) + sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=2"); + else + sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=1"); + + rc = write(*fd, mon_file_write_string, strlen(mon_file_write_string)+1); + if (rc < 0) + return -errno; + + return 0; +} + +/* + * Skips a specified number of lines in the buffer passed in. + * Walks the buffer pointed to by p and attempts to skip n lines. + * Returns the total number of lines skipped + */ +static int uv__skip_lines(char **p, int n) { + int lines = 0; + + while(n > 0) { + *p = strchr(*p, '\n'); + if (!p) + return lines; + + (*p)++; + n--; + lines++; + } + return lines; +} + + +/* + * Parse the event occurrence data to figure out what event just occurred + * and take proper action. + * + * The buf is a pointer to the buffer containing the event occurrence data + * Returns 0 on success, -1 if unrecoverable error in parsing + * + */ +static int uv__parse_data(char *buf, int *events, uv_fs_event_t* handle) { + int evp_rc, i; + char *p; + char filename[PATH_MAX]; /* To be used when handling directories */ + + p = buf; + *events = 0; + + /* Clean the filename buffer*/ + for(i = 0; i < PATH_MAX; i++) { + filename[i] = 0; + } + i = 0; + + /* Check for BUF_WRAP */ + if (strncmp(buf, "BUF_WRAP", strlen("BUF_WRAP")) == 0) { + assert(0 && "Buffer wrap detected, Some event occurrences lost!"); + return 0; + } + + /* Since we are using the default buffer size (4K), and have specified + * INFO_LVL=1, we won't see any EVENT_OVERFLOW conditions. Applications + * should check for this keyword if they are using an INFO_LVL of 2 or + * higher, and have a buffer size of <= 4K + */ + + /* Skip to RC_FROM_EVPROD */ + if (uv__skip_lines(&p, 9) != 9) + return -1; + + if (sscanf(p, "RC_FROM_EVPROD=%d\nEND_EVENT_DATA", &evp_rc) == 1) { + if (uv__path_is_a_directory(handle->path) == 0) { /* Directory */ + if (evp_rc == AHAFS_MODDIR_UNMOUNT || evp_rc == AHAFS_MODDIR_REMOVE_SELF) { + /* The directory is no longer available for monitoring */ + *events = UV_RENAME; + handle->dir_filename = NULL; + } else { + /* A file was added/removed inside the directory */ + *events = UV_CHANGE; + + /* Get the EVPROD_INFO */ + if (uv__skip_lines(&p, 1) != 1) + return -1; + + /* Scan out the name of the file that triggered the event*/ + if (sscanf(p, "BEGIN_EVPROD_INFO\n%sEND_EVPROD_INFO", filename) == 1) { + handle->dir_filename = strdup((const char*)&filename); + } else + return -1; + } + } else { /* Regular File */ + if (evp_rc == AHAFS_MODFILE_RENAME) + *events = UV_RENAME; + else + *events = UV_CHANGE; + } + } + else + return -1; + + return 0; +} + + +/* This is the internal callback */ +static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) { + char result_data[RDWR_BUF_SIZE]; + int bytes, rc = 0; + uv_fs_event_t* handle; + int events = 0; + int i = 0; + char fname[PATH_MAX]; + char *p; + + handle = container_of(event_watch, uv_fs_event_t, event_watcher); + + /* Clean all the buffers*/ + for(i = 0; i < PATH_MAX; i++) { + fname[i] = 0; + } + i = 0; + + /* At this point, we assume that polling has been done on the + * file descriptor, so we can just read the AHAFS event occurrence + * data and parse its results without having to block anything + */ + bytes = pread(event_watch->fd, result_data, RDWR_BUF_SIZE, 0); + + assert((bytes <= 0) && "uv__ahafs_event - Error reading monitor file"); + + /* Parse the data */ + if(bytes > 0) + rc = uv__parse_data(result_data, &events, handle); + + /* For directory changes, the name of the files that triggered the change + * are never absolute pathnames + */ + if (uv__path_is_a_directory(handle->path) == 0) { + p = handle->dir_filename; + while(*p != NULL){ + fname[i]= *p; + i++; + p++; + } + } else { + /* For file changes, figure out whether filename is absolute or not */ + if (handle->path[0] == '/') { + p = strrchr(handle->path, '/'); + p++; + + while(*p != NULL) { + fname[i]= *p; + i++; + p++; + } + } + } + + /* Unrecoverable error */ + if (rc == -1) + return; + else /* Call the actual JavaScript callback function */ + handle->cb(handle, (const char*)&fname, events, 0); +} + + int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { - return -ENOSYS; + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; } @@ -137,17 +871,99 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* filename, unsigned int flags) { - return -ENOSYS; + int fd, rc, i = 0, res = 0; + char cwd[PATH_MAX]; + char absolute_path[PATH_MAX]; + char fname[PATH_MAX]; + char *p; + + /* Clean all the buffers*/ + for(i = 0; i < PATH_MAX; i++) { + cwd[i] = 0; + absolute_path[i] = 0; + fname[i] = 0; + } + i = 0; + + /* Figure out whether filename is absolute or not */ + if (filename[0] == '/') { + /* We have absolute pathname, create the relative pathname*/ + sprintf(absolute_path, filename); + p = strrchr(filename, '/'); + p++; + } else { + if (filename[0] == '.' && filename[1] == '/') { + /* We have a relative pathname, compose the absolute pathname */ + sprintf(fname, filename); + snprintf(cwd, PATH_MAX-1, "/proc/%lu/cwd", (unsigned long) getpid()); + res = readlink(cwd, absolute_path, sizeof(absolute_path) - 1); + if (res < 0) + return res; + p = strrchr(absolute_path, '/'); + p++; + p++; + } else { + /* We have a relative pathname, compose the absolute pathname */ + sprintf(fname, filename); + snprintf(cwd, PATH_MAX-1, "/proc/%lu/cwd", (unsigned long) getpid()); + res = readlink(cwd, absolute_path, sizeof(absolute_path) - 1); + if (res < 0) + return res; + p = strrchr(absolute_path, '/'); + p++; + } + /* Copy to filename buffer */ + while(filename[i] != NULL) { + *p = filename[i]; + i++; + p++; + } + } + + if (uv__is_ahafs_mounted() < 0) /* /aha checks failed */ + return UV_ENOSYS; + + /* Setup ahafs */ + rc = uv__setup_ahafs((const char *)absolute_path, &fd); + if (rc != 0) + return rc; + + /* Setup/Initialize all the libuv routines */ + uv__handle_start(handle); + uv__io_init(&handle->event_watcher, uv__ahafs_event, fd); + handle->path = strdup((const char*)&absolute_path); + handle->cb = cb; + + uv__io_start(handle->loop, &handle->event_watcher, UV__POLLIN); + + return 0; } int uv_fs_event_stop(uv_fs_event_t* handle) { - return -ENOSYS; + + if (!uv__is_active(handle)) + return 0; + + uv__io_close(handle->loop, &handle->event_watcher); + uv__handle_stop(handle); + + if (uv__path_is_a_directory(handle->path) == 0) { + free(handle->dir_filename); + handle->dir_filename = NULL; + } + + free(handle->path); + handle->path = NULL; + uv__close(handle->event_watcher.fd); + handle->event_watcher.fd = -1; + + return 0; } void uv__fs_event_close(uv_fs_event_t* handle) { - UNREACHABLE(); + uv_fs_event_stop(handle); } @@ -175,7 +991,7 @@ int uv_resident_set_memory(size_t* rss) { int err; int fd; - (void) snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid()); + snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid()); fd = open(pp, O_RDONLY); if (fd == -1) @@ -397,3 +1213,21 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, free(addresses); } + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct pollfd* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct pollfd*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].fd == fd) + events[i].fd = -1; +} diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 402bb00eb2..4770d8d8c6 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -70,6 +70,10 @@ # endif #endif +#ifdef _AIX +#include +#endif + static void uv__run_pending(uv_loop_t* loop); /* Verify that uv_buf_t is ABI-compatible with struct iovec. */ @@ -444,7 +448,8 @@ int uv__close(int fd) { } -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \ + defined(_AIX) int uv__nonblock(int fd, int set) { int r; diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index 8a4edcbc0a..47f667229d 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -214,9 +214,23 @@ skip: } +static ssize_t uv__fs_mkdtemp(uv_fs_t* req) { + return mkdtemp((char*) req->path) ? 0 : -1; +} + + static ssize_t uv__fs_read(uv_fs_t* req) { ssize_t result; +#if defined(_AIX) + struct stat buf; + if(fstat(req->file, &buf)) + return -1; + if(S_ISDIR(buf.st_mode)) { + errno = EISDIR; + return -1; + } +#endif /* defined(_AIX) */ if (req->off < 0) { if (req->nbufs == 1) result = read(req->file, req->bufs[0].base, req->bufs[0].len); @@ -683,7 +697,8 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_birthtim.tv_nsec = src->st_birthtimespec.tv_nsec; dst->st_flags = src->st_flags; dst->st_gen = src->st_gen; -#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE) +#elif !defined(_AIX) && \ + (defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)) dst->st_atim.tv_sec = src->st_atim.tv_sec; dst->st_atim.tv_nsec = src->st_atim.tv_nsec; dst->st_mtim.tv_sec = src->st_mtim.tv_sec; @@ -779,6 +794,7 @@ static void uv__fs_work(struct uv__work* w) { X(LSTAT, uv__fs_lstat(req->path, &req->statbuf)); X(LINK, link(req->path, req->new_path)); X(MKDIR, mkdir(req->path, req->mode)); + X(MKDTEMP, uv__fs_mkdtemp(req)); X(READ, uv__fs_read(req)); X(READDIR, uv__fs_readdir(req)); X(READLINK, uv__fs_readlink(req)); @@ -991,6 +1007,18 @@ int uv_fs_mkdir(uv_loop_t* loop, } +int uv_fs_mkdtemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { + INIT(MKDTEMP); + req->path = strdup(tpl); + if (req->path == NULL) + return -ENOMEM; + POST; +} + + int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index fd29b88d42..114cb696ee 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -42,6 +42,12 @@ # include #endif /* __sun */ +#if defined(_AIX) +#define reqevents events +#define rtnevents revents +#include +#endif /* _AIX */ + #if defined(__APPLE__) && !TARGET_OS_IPHONE # include #endif @@ -89,7 +95,7 @@ # define UV__POLLHUP UV__EPOLLHUP #endif -#if defined(__sun) +#if defined(__sun) || defined(_AIX) # define UV__POLLIN POLLIN # define UV__POLLOUT POLLOUT # define UV__POLLERR POLLERR @@ -210,13 +216,6 @@ void uv__signal_close(uv_signal_t* handle); void uv__signal_global_once_init(void); void uv__signal_loop_cleanup(uv_loop_t* loop); -/* thread pool */ -void uv__work_submit(uv_loop_t* loop, - struct uv__work *w, - void (*work)(struct uv__work *w), - void (*done)(struct uv__work *w, int status)); -void uv__work_done(uv_async_t* handle); - /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); int uv__kqueue_init(uv_loop_t* loop); diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index 52c9328e75..aa74be6455 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -89,8 +89,10 @@ uv_loop_t* uv_loop_new(void) { void uv_loop_delete(uv_loop_t* loop) { uv_loop_t* default_loop; + int err; default_loop = default_loop_ptr; - assert(uv_loop_close(loop) == 0); + err = uv_loop_close(loop); + assert(err == 0); if (loop != default_loop) free(loop); } diff --git a/deps/uv/src/unix/openbsd.c b/deps/uv/src/unix/openbsd.c index 75ba921637..cde8d4d0c9 100644 --- a/deps/uv/src/unix/openbsd.c +++ b/deps/uv/src/unix/openbsd.c @@ -180,29 +180,23 @@ int uv_get_process_title(char* buffer, size_t size) { int uv_resident_set_memory(size_t* rss) { - kvm_t *kd = NULL; - struct kinfo_proc *kinfo = NULL; - pid_t pid; - int nprocs, max_size = sizeof(struct kinfo_proc); + struct kinfo_proc kinfo; size_t page_size = getpagesize(); + size_t size = sizeof(struct kinfo_proc); + int mib[6]; - pid = getpid(); + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + mib[4] = sizeof(struct kinfo_proc); + mib[5] = 1; - kd = kvm_open(NULL, _PATH_MEM, NULL, O_RDONLY, "kvm_open"); - if (kd == NULL) goto error; - - kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, max_size, &nprocs); - if (kinfo == NULL) goto error; - - *rss = kinfo->p_vm_rssize * page_size; - - kvm_close(kd); + if (sysctl(mib, 6, &kinfo, &size, NULL, 0) < 0) + return -errno; + *rss = kinfo.p_vm_rssize * page_size; return 0; - -error: - if (kd) kvm_close(kd); - return -EPERM; } diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 43334f0efb..ae7880c33f 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -60,21 +60,10 @@ 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 void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +static void uv__write_callbacks(uv_stream_t* stream); static size_t uv__write_req_size(uv_write_t* req); -static size_t uv_count_bufs(const uv_buf_t bufs[], unsigned int nbufs) { - unsigned int i; - size_t bytes; - - bytes = 0; - for (i = 0; i < nbufs; i++) - bytes += bufs[i].len; - - return bytes; -} - - void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type) { @@ -390,33 +379,12 @@ void uv__stream_destroy(uv_stream_t* stream) { QUEUE_REMOVE(q); req = QUEUE_DATA(q, uv_write_t, queue); - uv__req_unregister(stream->loop, req); + req->error = -ECANCELED; - if (req->bufs != req->bufsml) - free(req->bufs); - req->bufs = NULL; - - if (req->cb != NULL) - req->cb(req, -ECANCELED); + QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); } - while (!QUEUE_EMPTY(&stream->write_completed_queue)) { - q = QUEUE_HEAD(&stream->write_completed_queue); - QUEUE_REMOVE(q); - - req = QUEUE_DATA(q, uv_write_t, queue); - uv__req_unregister(stream->loop, req); - - if (req->bufs != NULL) { - stream->write_queue_size -= uv__write_req_size(req); - if (req->bufs != req->bufsml) - free(req->bufs); - req->bufs = NULL; - } - - if (req->cb) - req->cb(req, req->error); - } + uv__write_callbacks(stream); if (stream->shutdown_req) { /* The ECANCELED error code is a lie, the shutdown(2) syscall is a @@ -428,6 +396,8 @@ void uv__stream_destroy(uv_stream_t* stream) { stream->shutdown_req->cb(stream->shutdown_req, -ECANCELED); stream->shutdown_req = NULL; } + + assert(stream->write_queue_size == 0); } @@ -660,8 +630,8 @@ static size_t uv__write_req_size(uv_write_t* req) { size_t size; assert(req->bufs != NULL); - size = uv_count_bufs(req->bufs + req->write_index, - req->nbufs - req->write_index); + size = uv__count_bufs(req->bufs + req->write_index, + req->nbufs - req->write_index); assert(req->handle->write_queue_size >= size); return size; @@ -903,10 +873,6 @@ static void uv__write_callbacks(uv_stream_t* stream) { } assert(QUEUE_EMPTY(&stream->write_completed_queue)); - - /* Write queue drained. */ - if (QUEUE_EMPTY(&stream->write_queue)) - uv__drain(stream); } @@ -1223,6 +1189,10 @@ static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { if (events & (UV__POLLOUT | UV__POLLERR | UV__POLLHUP)) { uv__write(stream); uv__write_callbacks(stream); + + /* Write queue drained. */ + if (QUEUE_EMPTY(&stream->write_queue)) + uv__drain(stream); } } @@ -1327,7 +1297,7 @@ int uv_write2(uv_write_t* req, memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); req->nbufs = nbufs; req->write_index = 0; - stream->write_queue_size += uv_count_bufs(bufs, nbufs); + stream->write_queue_size += uv__count_bufs(bufs, nbufs); /* Append the request to write_queue. */ QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue); @@ -1386,7 +1356,7 @@ int uv_try_write(uv_stream_t* stream, /* Connecting or already writing some data */ if (stream->connect_req != NULL || stream->write_queue_size != 0) - return 0; + return -EAGAIN; has_pollout = uv__io_active(&stream->io_watcher, UV__POLLOUT); @@ -1395,7 +1365,7 @@ int uv_try_write(uv_stream_t* stream, return r; /* Remove not written bytes from write queue size */ - written = uv_count_bufs(bufs, nbufs); + written = uv__count_bufs(bufs, nbufs); if (req.bufs != NULL) req_size = uv__write_req_size(&req); else @@ -1416,7 +1386,10 @@ int uv_try_write(uv_stream_t* stream, uv__stream_osx_interrupt_select(stream); } - return (int) written; + if (written == 0) + return -EAGAIN; + else + return written; } diff --git a/deps/uv/src/unix/threadpool.c b/deps/uv/src/unix/threadpool.c deleted file mode 100644 index 18687249b1..0000000000 --- a/deps/uv/src/unix/threadpool.c +++ /dev/null @@ -1,284 +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 "internal.h" -#include - -#define MAX_THREADPOOL_SIZE 128 - -static uv_once_t once = UV_ONCE_INIT; -static uv_cond_t cond; -static uv_mutex_t mutex; -static unsigned int nthreads; -static uv_thread_t* threads; -static uv_thread_t default_threads[4]; -static QUEUE exit_message; -static QUEUE wq; -static volatile int initialized; - - -static void uv__cancelled(struct uv__work* w) { - abort(); -} - - -/* To avoid deadlock with uv_cancel() it's crucial that the worker - * never holds the global mutex and the loop-local mutex at the same time. - */ -static void worker(void* arg) { - struct uv__work* w; - QUEUE* q; - - (void) arg; - - for (;;) { - uv_mutex_lock(&mutex); - - while (QUEUE_EMPTY(&wq)) - uv_cond_wait(&cond, &mutex); - - q = QUEUE_HEAD(&wq); - - if (q == &exit_message) - uv_cond_signal(&cond); - else { - QUEUE_REMOVE(q); - QUEUE_INIT(q); /* Signal uv_cancel() that the work req is - executing. */ - } - - uv_mutex_unlock(&mutex); - - if (q == &exit_message) - break; - - w = QUEUE_DATA(q, struct uv__work, wq); - w->work(w); - - uv_mutex_lock(&w->loop->wq_mutex); - w->work = NULL; /* Signal uv_cancel() that the work req is done - executing. */ - QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq); - uv_async_send(&w->loop->wq_async); - uv_mutex_unlock(&w->loop->wq_mutex); - } -} - - -static void post(QUEUE* q) { - uv_mutex_lock(&mutex); - QUEUE_INSERT_TAIL(&wq, q); - uv_cond_signal(&cond); - uv_mutex_unlock(&mutex); -} - - -static void init_once(void) { - unsigned int i; - const char* val; - - nthreads = ARRAY_SIZE(default_threads); - val = getenv("UV_THREADPOOL_SIZE"); - if (val != NULL) - nthreads = atoi(val); - if (nthreads == 0) - nthreads = 1; - if (nthreads > MAX_THREADPOOL_SIZE) - nthreads = MAX_THREADPOOL_SIZE; - - threads = default_threads; - if (nthreads > ARRAY_SIZE(default_threads)) { - threads = malloc(nthreads * sizeof(threads[0])); - if (threads == NULL) { - nthreads = ARRAY_SIZE(default_threads); - threads = default_threads; - } - } - - if (uv_cond_init(&cond)) - abort(); - - if (uv_mutex_init(&mutex)) - abort(); - - QUEUE_INIT(&wq); - - for (i = 0; i < nthreads; i++) - if (uv_thread_create(threads + i, worker, NULL)) - abort(); - - initialized = 1; -} - - -UV_DESTRUCTOR(static void cleanup(void)) { - unsigned int i; - - if (initialized == 0) - return; - - post(&exit_message); - - for (i = 0; i < nthreads; i++) - if (uv_thread_join(threads + i)) - abort(); - - if (threads != default_threads) - free(threads); - - uv_mutex_destroy(&mutex); - uv_cond_destroy(&cond); - - threads = NULL; - nthreads = 0; - initialized = 0; -} - - -void uv__work_submit(uv_loop_t* loop, - struct uv__work* w, - void (*work)(struct uv__work* w), - void (*done)(struct uv__work* w, int status)) { - uv_once(&once, init_once); - w->loop = loop; - w->work = work; - w->done = done; - post(&w->wq); -} - - -static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { - int cancelled; - - uv_mutex_lock(&mutex); - uv_mutex_lock(&w->loop->wq_mutex); - - cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL; - if (cancelled) - QUEUE_REMOVE(&w->wq); - - uv_mutex_unlock(&w->loop->wq_mutex); - uv_mutex_unlock(&mutex); - - if (!cancelled) - return -EBUSY; - - w->work = uv__cancelled; - uv_mutex_lock(&loop->wq_mutex); - QUEUE_INSERT_TAIL(&loop->wq, &w->wq); - uv_async_send(&loop->wq_async); - uv_mutex_unlock(&loop->wq_mutex); - - return 0; -} - - -void uv__work_done(uv_async_t* handle) { - struct uv__work* w; - uv_loop_t* loop; - QUEUE* q; - QUEUE wq; - int err; - - loop = container_of(handle, uv_loop_t, wq_async); - QUEUE_INIT(&wq); - - uv_mutex_lock(&loop->wq_mutex); - if (!QUEUE_EMPTY(&loop->wq)) { - q = QUEUE_HEAD(&loop->wq); - QUEUE_SPLIT(&loop->wq, q, &wq); - } - uv_mutex_unlock(&loop->wq_mutex); - - while (!QUEUE_EMPTY(&wq)) { - q = QUEUE_HEAD(&wq); - QUEUE_REMOVE(q); - - w = container_of(q, struct uv__work, wq); - err = (w->work == uv__cancelled) ? -ECANCELED : 0; - w->done(w, err); - } -} - - -static void uv__queue_work(struct uv__work* w) { - uv_work_t* req = container_of(w, uv_work_t, work_req); - - req->work_cb(req); -} - - -static void uv__queue_done(struct uv__work* w, int err) { - uv_work_t* req; - - req = container_of(w, uv_work_t, work_req); - uv__req_unregister(req->loop, req); - - if (req->after_work_cb == NULL) - return; - - req->after_work_cb(req, err); -} - - -int uv_queue_work(uv_loop_t* loop, - uv_work_t* req, - uv_work_cb work_cb, - uv_after_work_cb after_work_cb) { - if (work_cb == NULL) - return -EINVAL; - - uv__req_init(loop, req, UV_WORK); - req->loop = loop; - req->work_cb = work_cb; - req->after_work_cb = after_work_cb; - uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done); - return 0; -} - - -int uv_cancel(uv_req_t* req) { - struct uv__work* wreq; - uv_loop_t* loop; - - switch (req->type) { - case UV_FS: - loop = ((uv_fs_t*) req)->loop; - wreq = &((uv_fs_t*) req)->work_req; - break; - case UV_GETADDRINFO: - loop = ((uv_getaddrinfo_t*) req)->loop; - wreq = &((uv_getaddrinfo_t*) req)->work_req; - break; - case UV_GETNAMEINFO: - loop = ((uv_getnameinfo_t*) req)->loop; - wreq = &((uv_getnameinfo_t*) req)->work_req; - break; - case UV_WORK: - loop = ((uv_work_t*) req)->loop; - wreq = &((uv_work_t*) req)->work_req; - break; - default: - return -EINVAL; - } - - return uv__work_cancel(loop, req, wreq); -} diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index 9556bd7e3b..bf91cbdf9f 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -38,10 +38,9 @@ static void uv__udp_run_completed(uv_udp_t* handle); -static void uv__udp_run_pending(uv_udp_t* handle); static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents); -static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents); -static void uv__udp_sendmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents); +static void uv__udp_recvmsg(uv_udp_t* handle); +static void uv__udp_sendmsg(uv_udp_t* handle); static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain, unsigned int flags); @@ -65,22 +64,19 @@ void uv__udp_finish_close(uv_udp_t* handle) { assert(!uv__io_active(&handle->io_watcher, UV__POLLIN | UV__POLLOUT)); assert(handle->io_watcher.fd == -1); - uv__udp_run_completed(handle); - while (!QUEUE_EMPTY(&handle->write_queue)) { q = QUEUE_HEAD(&handle->write_queue); QUEUE_REMOVE(q); req = QUEUE_DATA(q, uv_udp_send_t, queue); - uv__req_unregister(handle->loop, req); + req->status = -ECANCELED; + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + } - if (req->bufs != req->bufsml) - free(req->bufs); - req->bufs = NULL; + uv__udp_run_completed(handle); - if (req->send_cb != NULL) - req->send_cb(req, -ECANCELED); - } + assert(handle->send_queue_size == 0); + assert(handle->send_queue_count == 0); /* Now tear down the handle. */ handle->recv_cb = NULL; @@ -89,50 +85,6 @@ void uv__udp_finish_close(uv_udp_t* handle) { } -static void uv__udp_run_pending(uv_udp_t* handle) { - uv_udp_send_t* req; - QUEUE* q; - struct msghdr h; - ssize_t size; - - while (!QUEUE_EMPTY(&handle->write_queue)) { - q = QUEUE_HEAD(&handle->write_queue); - assert(q != NULL); - - req = 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->addr.sin6_family == AF_INET6 ? - sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - h.msg_iov = (struct iovec*) req->bufs; - h.msg_iovlen = req->nbufs; - - do { - size = sendmsg(handle->io_watcher.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); - - /* 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. - */ - QUEUE_REMOVE(&req->queue); - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); - } -} - - static void uv__udp_run_completed(uv_udp_t* handle) { uv_udp_send_t* req; QUEUE* q; @@ -144,6 +96,9 @@ static void uv__udp_run_completed(uv_udp_t* handle) { req = QUEUE_DATA(q, uv_udp_send_t, queue); uv__req_unregister(handle->loop, req); + handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs); + handle->send_queue_count--; + if (req->bufs != req->bufsml) free(req->bufs); req->bufs = NULL; @@ -159,33 +114,40 @@ static void uv__udp_run_completed(uv_udp_t* handle) { else req->send_cb(req, req->status); } + + if (QUEUE_EMPTY(&handle->write_queue)) { + /* Pending queue and completion queue empty, stop watcher. */ + uv__io_stop(handle->loop, &handle->io_watcher, UV__POLLOUT); + if (!uv__io_active(&handle->io_watcher, UV__POLLIN)) + uv__handle_stop(handle); + } } static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { + uv_udp_t* handle; + + handle = container_of(w, uv_udp_t, io_watcher); + assert(handle->type == UV_UDP); + if (revents & UV__POLLIN) - uv__udp_recvmsg(loop, w, revents); + uv__udp_recvmsg(handle); - if (revents & UV__POLLOUT) - uv__udp_sendmsg(loop, w, revents); + if (revents & UV__POLLOUT) { + uv__udp_sendmsg(handle); + uv__udp_run_completed(handle); + } } -static void uv__udp_recvmsg(uv_loop_t* loop, - uv__io_t* w, - unsigned int revents) { +static void uv__udp_recvmsg(uv_udp_t* handle) { struct sockaddr_storage peer; struct msghdr h; - uv_udp_t* handle; ssize_t nread; uv_buf_t buf; int flags; int count; - handle = container_of(w, uv_udp_t, io_watcher); - assert(handle->type == UV_UDP); - assert(revents & UV__POLLIN); - assert(handle->recv_cb != NULL); assert(handle->alloc_cb != NULL); @@ -242,34 +204,43 @@ static void uv__udp_recvmsg(uv_loop_t* loop, } -static void uv__udp_sendmsg(uv_loop_t* loop, - uv__io_t* w, - unsigned int revents) { - uv_udp_t* handle; +static void uv__udp_sendmsg(uv_udp_t* handle) { + uv_udp_send_t* req; + QUEUE* q; + struct msghdr h; + ssize_t size; - handle = container_of(w, uv_udp_t, io_watcher); - assert(handle->type == UV_UDP); - assert(revents & UV__POLLOUT); + while (!QUEUE_EMPTY(&handle->write_queue)) { + q = QUEUE_HEAD(&handle->write_queue); + assert(q != NULL); + + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); - assert(!QUEUE_EMPTY(&handle->write_queue) - || !QUEUE_EMPTY(&handle->write_completed_queue)); + memset(&h, 0, sizeof h); + h.msg_name = &req->addr; + h.msg_namelen = (req->addr.ss_family == AF_INET6 ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); + h.msg_iov = (struct iovec*) req->bufs; + h.msg_iovlen = req->nbufs; - /* Write out pending data first. */ - uv__udp_run_pending(handle); + do { + size = sendmsg(handle->io_watcher.fd, &h, 0); + } while (size == -1 && errno == EINTR); - /* Drain 'request completed' queue. */ - uv__udp_run_completed(handle); + if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + break; - if (!QUEUE_EMPTY(&handle->write_completed_queue)) { - /* Schedule completion callbacks. */ - uv__io_feed(handle->loop, &handle->io_watcher); - } - else if (QUEUE_EMPTY(&handle->write_queue)) { - /* Pending queue and completion queue empty, stop watcher. */ - uv__io_stop(loop, &handle->io_watcher, UV__POLLOUT); + req->status = (size == -1 ? -errno : size); - if (!uv__io_active(&handle->io_watcher, UV__POLLIN)) - uv__handle_stop(handle); + /* 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. + */ + QUEUE_REMOVE(&req->queue); + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + uv__io_feed(handle->loop, &handle->io_watcher); } } @@ -410,6 +381,7 @@ int uv__udp_send(uv_udp_send_t* req, unsigned int addrlen, uv_udp_send_cb send_cb) { int err; + int empty_queue; assert(nbufs > 0); @@ -417,8 +389,13 @@ int uv__udp_send(uv_udp_send_t* req, if (err) return err; - uv__req_init(handle->loop, req, UV_UDP_SEND); + /* It's legal for send_queue_count > 0 even when the write_queue is empty; + * it means there are error-state requests in the write_completed_queue that + * will touch up send_queue_size/count later. + */ + empty_queue = (handle->send_queue_count == 0); + uv__req_init(handle->loop, req, UV_UDP_SEND); assert(addrlen <= sizeof(req->addr)); memcpy(&req->addr, addr, addrlen); req->send_cb = send_cb; @@ -433,14 +410,60 @@ int uv__udp_send(uv_udp_send_t* req, return -ENOMEM; memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); + handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs); + handle->send_queue_count++; QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue); - uv__io_start(handle->loop, &handle->io_watcher, UV__POLLOUT); uv__handle_start(handle); + if (empty_queue) + uv__udp_sendmsg(handle); + else + uv__io_start(handle->loop, &handle->io_watcher, UV__POLLOUT); + return 0; } +int uv__udp_try_send(uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr, + unsigned int addrlen) { + int err; + struct msghdr h; + ssize_t size; + + assert(nbufs > 0); + + /* already sending a message */ + if (handle->send_queue_count != 0) + return -EAGAIN; + + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + + memset(&h, 0, sizeof h); + h.msg_name = (struct sockaddr*) addr; + h.msg_namelen = addrlen; + h.msg_iov = (struct iovec*) bufs; + h.msg_iovlen = nbufs; + + do { + size = sendmsg(handle->io_watcher.fd, &h, 0); + } while (size == -1 && errno == EINTR); + + if (size == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + return -EAGAIN; + else + return -errno; + } + + return size; +} + + static int uv__udp_set_membership4(uv_udp_t* handle, const struct sockaddr_in* multicast_addr, const char* interface_addr, @@ -531,6 +554,8 @@ int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP); handle->alloc_cb = NULL; handle->recv_cb = NULL; + handle->send_queue_size = 0; + handle->send_queue_count = 0; uv__io_init(&handle->io_watcher, uv__udp_io, -1); QUEUE_INIT(&handle->write_queue); QUEUE_INIT(&handle->write_completed_queue); @@ -579,7 +604,7 @@ int uv_udp_set_membership(uv_udp_t* handle, static int uv__setsockopt_maybe_char(uv_udp_t* handle, int option, int val) { -#if defined(__sun) +#if defined(__sun) || defined(_AIX) char arg = val; #else int arg = val; @@ -678,7 +703,9 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) } -int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, int* namelen) { +int uv_udp_getsockname(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { socklen_t socklen; if (handle->io_watcher.fd == -1) diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index d9553c9162..4e3968cb44 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -233,6 +233,26 @@ int uv_udp_send(uv_udp_send_t* req, } +int uv_udp_try_send(uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr) { + unsigned int addrlen; + + if (handle->type != UV_UDP) + return UV_EINVAL; + + if (addr->sa_family == AF_INET) + addrlen = sizeof(struct sockaddr_in); + else if (addr->sa_family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); + else + return UV_EINVAL; + + return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen); +} + + int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { @@ -446,6 +466,19 @@ int uv__getaddrinfo_translate_error(int sys_err) { return 0; /* Pacify compiler. */ } + +size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs) { + unsigned int i; + size_t bytes; + + bytes = 0; + for (i = 0; i < nbufs; i++) + bytes += (size_t) bufs[i].len; + + return bytes; +} + + int uv_fs_event_getpath(uv_fs_event_t* handle, char* buf, size_t* len) { size_t required_len; diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index 3bcdcef3d4..34c287898c 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -83,6 +83,12 @@ int uv__udp_send(uv_udp_send_t* req, unsigned int addrlen, uv_udp_send_cb send_cb); +int uv__udp_try_send(uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr, + unsigned int addrlen); + int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb, uv_udp_recv_cb recv_cb); @@ -92,6 +98,15 @@ void uv__fs_poll_close(uv_fs_poll_t* handle); int uv__getaddrinfo_translate_error(int sys_err); /* EAI_* error. */ +void uv__work_submit(uv_loop_t* loop, + struct uv__work *w, + void (*work)(struct uv__work *w), + void (*done)(struct uv__work *w, int status)); + +void uv__work_done(uv_async_t* handle); + +size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs); + #define uv__has_active_reqs(loop) \ (QUEUE_EMPTY(&(loop)->active_reqs) == 0) diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index 540fb5fa0d..c39597561d 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -26,7 +26,7 @@ #include #include #include -#if !defined(__MINGW32__) +#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) #include #endif @@ -44,19 +44,21 @@ static uv_once_t uv_init_guard_ = UV_ONCE_INIT; static uv_once_t uv_default_loop_init_guard_ = UV_ONCE_INIT; -#if defined(_DEBUG) && !defined(__MINGW32__) -/* Our crt debug report handler allows us to temporarily disable asserts */ -/* just for the current thread. */ +#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)) +/* Our crt debug report handler allows us to temporarily disable asserts + * just for the current thread. + */ -__declspec( thread ) int uv__crt_assert_enabled = TRUE; +UV_THREAD_LOCAL int uv__crt_assert_enabled = TRUE; static int uv__crt_dbg_report_handler(int report_type, char *message, int *ret_val) { if (uv__crt_assert_enabled || report_type != _CRT_ASSERT) return FALSE; if (ret_val) { - /* Set ret_val to 0 to continue with normal execution. */ - /* Set ret_val to 1 to trigger a breakpoint. */ + /* Set ret_val to 0 to continue with normal execution. + * Set ret_val to 1 to trigger a breakpoint. + */ if(IsDebuggerPresent()) *ret_val = 1; @@ -67,6 +69,8 @@ static int uv__crt_dbg_report_handler(int report_type, char *message, int *ret_v /* Don't call _CrtDbgReport. */ return TRUE; } +#else +UV_THREAD_LOCAL int uv__crt_assert_enabled = FALSE; #endif @@ -84,21 +88,24 @@ static void uv_init(void) { SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); - /* Tell the CRT to not exit the application when an invalid parameter is */ - /* passed. The main issue is that invalid FDs will trigger this behavior. */ + /* Tell the CRT to not exit the application when an invalid parameter is + * passed. The main issue is that invalid FDs will trigger this behavior. + */ #if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800 _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler); #endif - /* We also need to setup our debug report handler because some CRT */ - /* functions (eg _get_osfhandle) raise an assert when called with invalid */ - /* FDs even though they return the proper error code in the release build. */ -#if defined(_DEBUG) && !defined(__MINGW32__) + /* We also need to setup our debug report handler because some CRT + * functions (eg _get_osfhandle) raise an assert when called with invalid + * FDs even though they return the proper error code in the release build. + */ +#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)) _CrtSetReportHook(uv__crt_dbg_report_handler); #endif - /* Fetch winapi function pointers. This must be done first because other */ - /* intialization code might need these function pointers to be loaded. */ + /* Fetch winapi function pointers. This must be done first because other + * intialization code might need these function pointers to be loaded. + */ uv_winapi_init(); /* Initialize winsock */ @@ -127,12 +134,14 @@ int uv_loop_init(uv_loop_t* loop) { if (loop->iocp == NULL) return uv_translate_sys_error(GetLastError()); - /* To prevent uninitialized memory access, loop->time must be intialized */ - /* to zero before calling uv_update_time for the first time. */ + /* To prevent uninitialized memory access, loop->time must be intialized + * to zero before calling uv_update_time for the first time. + */ loop->time = 0; loop->last_tick_count = 0; uv_update_time(loop); + QUEUE_INIT(&loop->wq); QUEUE_INIT(&loop->handle_queue); QUEUE_INIT(&loop->active_reqs); loop->active_handles = 0; @@ -159,6 +168,15 @@ int uv_loop_init(uv_loop_t* loop) { loop->timer_counter = 0; loop->stop_flag = 0; + if (uv_mutex_init(&loop->wq_mutex)) + abort(); + + if (uv_async_init(loop, &loop->wq_async, uv__work_done)) + abort(); + + uv__handle_unref(&loop->wq_async); + loop->wq_async.flags |= UV__HANDLE_INTERNAL; + return 0; } @@ -183,6 +201,31 @@ uv_loop_t* uv_default_loop(void) { } +static void uv__loop_close(uv_loop_t* loop) { + /* close the async handle without needeing an extra loop iteration */ + assert(!loop->wq_async.async_sent); + loop->wq_async.close_cb = NULL; + uv__handle_closing(&loop->wq_async); + uv__handle_close(&loop->wq_async); + + if (loop != &uv_default_loop_) { + size_t i; + for (i = 0; i < ARRAY_SIZE(loop->poll_peer_sockets); i++) { + SOCKET sock = loop->poll_peer_sockets[i]; + if (sock != 0 && sock != INVALID_SOCKET) + closesocket(sock); + } + } + /* TODO: cleanup default loop*/ + + uv_mutex_lock(&loop->wq_mutex); + assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); + assert(!uv__has_active_reqs(loop)); + uv_mutex_unlock(&loop->wq_mutex); + uv_mutex_destroy(&loop->wq_mutex); +} + + int uv_loop_close(uv_loop_t* loop) { QUEUE* q; uv_handle_t* h; @@ -193,15 +236,13 @@ int uv_loop_close(uv_loop_t* loop) { if (!(h->flags & UV__HANDLE_INTERNAL)) return UV_EBUSY; } - if (loop != &uv_default_loop_) { - size_t i; - for (i = 0; i < ARRAY_SIZE(loop->poll_peer_sockets); i++) { - SOCKET sock = loop->poll_peer_sockets[i]; - if (sock != 0 && sock != INVALID_SOCKET) - closesocket(sock); - } - } - /* TODO: cleanup default loop*/ + + uv__loop_close(loop); + +#ifndef NDEBUG + memset(loop, -1, sizeof(*loop)); +#endif + return 0; } @@ -224,7 +265,8 @@ uv_loop_t* uv_loop_new(void) { void uv_loop_delete(uv_loop_t* loop) { - assert(uv_loop_close(loop) == 0); + int err = uv_loop_close(loop); + assert(err == 0); if (loop != &uv_default_loop_) free(loop); } @@ -236,22 +278,31 @@ int uv_backend_fd(const uv_loop_t* loop) { int uv_backend_timeout(const uv_loop_t* loop) { - return 0; + if (loop->stop_flag != 0) + return 0; + + if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) + return 0; + + if (loop->pending_reqs_tail) + return 0; + + if (loop->endgame_handles) + return 0; + + if (loop->idle_handles) + return 0; + + return uv__next_timeout(loop); } -static void uv_poll(uv_loop_t* loop, int block) { - DWORD bytes, timeout; +static void uv_poll(uv_loop_t* loop, DWORD timeout) { + DWORD bytes; ULONG_PTR key; OVERLAPPED* overlapped; uv_req_t* req; - if (block) { - timeout = uv_get_poll_timeout(loop); - } else { - timeout = 0; - } - GetQueuedCompletionStatus(loop->iocp, &bytes, &key, @@ -266,28 +317,22 @@ static void uv_poll(uv_loop_t* loop, int block) { /* Serious error */ uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); } else { - /* We're sure that at least `timeout` milliseconds have expired, but */ - /* this may not be reflected yet in the GetTickCount() return value. */ - /* Therefore we ensure it's taken into account here. */ + /* We're sure that at least `timeout` milliseconds have expired, but + * this may not be reflected yet in the GetTickCount() return value. + * Therefore we ensure it's taken into account here. + */ uv__time_forward(loop, timeout); } } -static void uv_poll_ex(uv_loop_t* loop, int block) { +static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) { BOOL success; - DWORD timeout; uv_req_t* req; OVERLAPPED_ENTRY overlappeds[128]; ULONG count; ULONG i; - if (block) { - timeout = uv_get_poll_timeout(loop); - } else { - timeout = 0; - } - success = pGetQueuedCompletionStatusEx(loop->iocp, overlappeds, ARRAY_SIZE(overlappeds), @@ -305,9 +350,10 @@ static void uv_poll_ex(uv_loop_t* loop, int block) { /* Serious error */ uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx"); } else if (timeout > 0) { - /* We're sure that at least `timeout` milliseconds have expired, but */ - /* this may not be reflected yet in the GetTickCount() return value. */ - /* Therefore we ensure it's taken into account here. */ + /* We're sure that at least `timeout` milliseconds have expired, but + * this may not be reflected yet in the GetTickCount() return value. + * Therefore we ensure it's taken into account here. + */ uv__time_forward(loop, timeout); } } @@ -326,8 +372,9 @@ int uv_loop_alive(const uv_loop_t* loop) { int uv_run(uv_loop_t *loop, uv_run_mode mode) { + DWORD timeout; int r; - void (*poll)(uv_loop_t* loop, int block); + void (*poll)(uv_loop_t* loop, DWORD timeout); if (pGetQueuedCompletionStatusEx) poll = &uv_poll_ex; @@ -346,13 +393,11 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { uv_idle_invoke(loop); uv_prepare_invoke(loop); - (*poll)(loop, loop->idle_handles == NULL && - loop->pending_reqs_tail == NULL && - loop->endgame_handles == NULL && - !loop->stop_flag && - (loop->active_handles > 0 || - !QUEUE_EMPTY(&loop->active_reqs)) && - !(mode & UV_RUN_NOWAIT)); + timeout = 0; + if ((mode & UV_RUN_NOWAIT) == 0) + timeout = uv_backend_timeout(loop); + + (*poll)(loop, timeout); uv_check_invoke(loop); uv_process_endgames(loop); diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c index 3162bc787f..5c5514736e 100644 --- a/deps/uv/src/win/error.c +++ b/deps/uv/src/win/error.c @@ -133,6 +133,7 @@ int uv_translate_sys_error(int sys_errno) { case ERROR_DIRECTORY: return UV_ENOENT; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_INVALID_NAME: return UV_ENOENT; + case ERROR_INVALID_DRIVE: return UV_ENOENT; case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT; case ERROR_MOD_NOT_FOUND: return UV_ENOENT; case ERROR_PATH_NOT_FOUND: return UV_ENOENT; diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index f31c0a2999..8b52e610f4 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -44,12 +44,8 @@ #define QUEUE_FS_TP_JOB(loop, req) \ do { \ - if (!QueueUserWorkItem(&uv_fs_thread_proc, \ - req, \ - WT_EXECUTEDEFAULT)) { \ - return uv_translate_sys_error(GetLastError()); \ - } \ uv__req_register(loop, req); \ + uv__work_submit((loop), &(req)->work_req, uv__fs_work, uv__fs_done); \ } while (0) #define SET_REQ_RESULT(req, result_value) \ @@ -232,11 +228,7 @@ INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, req->result = 0; req->ptr = NULL; req->path = NULL; - - if (cb != NULL) { - req->cb = cb; - memset(&req->overlapped, 0, sizeof(req->overlapped)); - } + req->cb = cb; } @@ -729,6 +721,78 @@ void fs__mkdir(uv_fs_t* req) { } +/* Some parts of the implementation were borrowed from glibc. */ +void fs__mkdtemp(uv_fs_t* req) { + static const WCHAR letters[] = + L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + size_t len; + WCHAR* template_part; + static uint64_t value; + unsigned int count; + int fd; + + /* A lower bound on the number of temporary files to attempt to + generate. The maximum total number of temporary file names that + can exist for a given template is 62**6. It should never be + necessary to try all these combinations. Instead if a reasonable + number of names is tried (we define reasonable as 62**3) fail to + give the system administrator the chance to remove the problems. */ +#define ATTEMPTS_MIN (62 * 62 * 62) + + /* The number of times to attempt to generate a temporary file. To + conform to POSIX, this must be no smaller than TMP_MAX. */ +#if ATTEMPTS_MIN < TMP_MAX + unsigned int attempts = TMP_MAX; +#else + unsigned int attempts = ATTEMPTS_MIN; +#endif + + len = wcslen(req->pathw); + if (len < 6 || wcsncmp(&req->pathw[len - 6], L"XXXXXX", 6)) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); + return; + } + + /* This is where the Xs start. */ + template_part = &req->pathw[len - 6]; + + /* Get some random data. */ + value += uv_hrtime() ^ _getpid(); + + for (count = 0; count < attempts; value += 7777, ++count) { + uint64_t v = value; + + /* Fill in the random bits. */ + template_part[0] = letters[v % 62]; + v /= 62; + template_part[1] = letters[v % 62]; + v /= 62; + template_part[2] = letters[v % 62]; + v /= 62; + template_part[3] = letters[v % 62]; + v /= 62; + template_part[4] = letters[v % 62]; + v /= 62; + template_part[5] = letters[v % 62]; + + fd = _wmkdir(req->pathw); + + if (fd >= 0) { + len = strlen(req->path); + wcstombs((char*) req->path + len - 6, template_part, 6); + SET_REQ_RESULT(req, 0); + return; + } else if (errno != EEXIST) { + SET_REQ_RESULT(req, -1); + return; + } + } + + /* We got out of the loop because we ran out of combinations to try. */ + SET_REQ_RESULT(req, -1); +} + + void fs__readdir(uv_fs_t* req) { WCHAR* pathw = req->pathw; size_t len = wcslen(pathw); @@ -1401,7 +1465,7 @@ static void fs__create_junction(uv_fs_t* req, const WCHAR* path, /* Open the directory */ handle = CreateFileW(new_path, - GENERIC_ALL, + GENERIC_WRITE, 0, NULL, OPEN_EXISTING, @@ -1510,11 +1574,10 @@ static void fs__fchown(uv_fs_t* req) { } -static DWORD WINAPI uv_fs_thread_proc(void* parameter) { - uv_fs_t* req = (uv_fs_t*) parameter; - uv_loop_t* loop = req->loop; +static void uv__fs_work(struct uv__work* w) { + uv_fs_t* req; - assert(req != NULL); + req = container_of(w, uv_fs_t, work_req); assert(req->type == UV_FS); #define XX(uc, lc) case UV_FS_##uc: fs__##lc(req); break; @@ -1537,6 +1600,7 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) { XX(UNLINK, unlink) XX(RMDIR, rmdir) XX(MKDIR, mkdir) + XX(MKDTEMP, mkdtemp) XX(RENAME, rename) XX(READDIR, readdir) XX(LINK, link) @@ -1547,9 +1611,41 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) { default: assert(!"bad uv_fs_type"); } +} - POST_COMPLETION_FOR_REQ(loop, req); - return 0; + +static void uv__fs_done(struct uv__work* w, int status) { + uv_fs_t* req; + + req = container_of(w, uv_fs_t, work_req); + uv__req_unregister(req->loop, req); + + if (status == UV_ECANCELED) { + assert(req->result == 0); + req->result = UV_ECANCELED; + } + + if (req->cb != NULL) + req->cb(req); +} + + +void uv_fs_req_cleanup(uv_fs_t* req) { + if (req->flags & UV_FS_CLEANEDUP) + return; + + if (req->flags & UV_FS_FREE_PATHS) + free(req->pathw); + + if (req->flags & UV_FS_FREE_PTR) + free(req->ptr); + + req->path = NULL; + req->pathw = NULL; + req->new_pathw = NULL; + req->ptr = NULL; + + req->flags |= UV_FS_CLEANEDUP; } @@ -1701,6 +1797,26 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, } +int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, + uv_fs_cb cb) { + int err; + + uv_fs_req_init(loop, req, UV_FS_MKDTEMP, cb); + + err = fs__capture_path(loop, req, tpl, NULL, TRUE); + if (err) + return uv_translate_sys_error(err); + + if (cb) { + QUEUE_FS_TP_JOB(loop, req); + return 0; + } else { + fs__mkdtemp(req); + return req->result; + } +} + + int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; @@ -2064,30 +2180,3 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, return req->result; } } - - -void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req) { - assert(req->cb); - uv__req_unregister(loop, req); - req->cb(req); -} - - -void uv_fs_req_cleanup(uv_fs_t* req) { - if (req->flags & UV_FS_CLEANEDUP) - return; - - if (req->flags & UV_FS_FREE_PATHS) - free(req->pathw); - - if (req->flags & UV_FS_FREE_PTR) - free(req->ptr); - - req->path = NULL; - req->pathw = NULL; - req->new_pathw = NULL; - req->ptr = NULL; - - req->flags |= UV_FS_CLEANEDUP; -} - diff --git a/deps/uv/src/win/getaddrinfo.c b/deps/uv/src/win/getaddrinfo.c index b87a933f0f..086200a9ea 100644 --- a/deps/uv/src/win/getaddrinfo.c +++ b/deps/uv/src/win/getaddrinfo.c @@ -56,25 +56,13 @@ #define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) -/* getaddrinfo worker thread implementation */ -static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) { - uv_getaddrinfo_t* req = (uv_getaddrinfo_t*) parameter; - uv_loop_t* loop = req->loop; - int ret; - - assert(req != NULL); - - /* call OS function on this thread */ - ret = GetAddrInfoW(req->node, - req->service, - req->hints, - &req->res); - req->retcode = ret; - - /* post getaddrinfo completed */ - POST_COMPLETION_FOR_REQ(loop, req); +static void uv__getaddrinfo_work(struct uv__work* w) { + uv_getaddrinfo_t* req; + int err; - return 0; + req = container_of(w, uv_getaddrinfo_t, work_req); + err = GetAddrInfoW(req->node, req->service, req->hints, &req->res); + req->retcode = uv__getaddrinfo_translate_error(err); } @@ -87,7 +75,8 @@ static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) { * 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* req) { +static void uv__getaddrinfo_done(struct uv__work* w, int status) { + uv_getaddrinfo_t* req; int addrinfo_len = 0; int name_len = 0; size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo)); @@ -95,7 +84,8 @@ void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req) { struct addrinfo* addrinfo_ptr; char* alloc_ptr = NULL; char* cur_ptr = NULL; - int err = 0; + + req = container_of(w, uv_getaddrinfo_t, work_req); /* release input parameter memory */ if (req->alloc != NULL) { @@ -103,6 +93,16 @@ void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req) { req->alloc = NULL; } + if (status == UV_ECANCELED) { + assert(req->retcode == 0); + req->retcode = UV_EAI_CANCELED; + if (req->res != NULL) { + FreeAddrInfoW(req->res); + req->res = NULL; + } + goto complete; + } + if (req->retcode == 0) { /* convert addrinfoW to addrinfo */ /* first calculate required length */ @@ -113,7 +113,7 @@ void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req) { if (addrinfow_ptr->ai_canonname != NULL) { name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); if (name_len == 0) { - err = uv_translate_sys_error(GetLastError()); + req->retcode = uv_translate_sys_error(GetLastError()); goto complete; } addrinfo_len += ALIGNED_SIZE(name_len); @@ -178,11 +178,8 @@ void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req) { } } } else { - err = UV_EAI_MEMORY; + req->retcode = UV_EAI_MEMORY; } - } else { - /* GetAddrInfo failed */ - err = uv__getaddrinfo_translate_error(req->retcode); } /* return memory to system */ @@ -192,10 +189,10 @@ void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req) { } complete: - uv__req_unregister(loop, req); + uv__req_unregister(req->loop, req); /* finally do callback with converted result */ - req->getaddrinfo_cb(req, err, (struct addrinfo*)alloc_ptr); + req->getaddrinfo_cb(req, req->retcode, (struct addrinfo*)alloc_ptr); } @@ -246,6 +243,7 @@ int uv_getaddrinfo(uv_loop_t* loop, req->res = NULL; req->type = UV_GETADDRINFO; req->loop = loop; + req->retcode = 0; /* calculate required memory size for all input values */ if (node != NULL) { @@ -323,13 +321,10 @@ int uv_getaddrinfo(uv_loop_t* loop, req->hints = NULL; } - /* Ask thread to run. Treat this as a long operation */ - if (QueueUserWorkItem(&getaddrinfo_thread_proc, - req, - WT_EXECUTELONGFUNCTION) == 0) { - err = GetLastError(); - goto error; - } + uv__work_submit(loop, + &req->work_req, + uv__getaddrinfo_work, + uv__getaddrinfo_done); uv__req_register(loop, req); diff --git a/deps/uv/src/win/getnameinfo.c b/deps/uv/src/win/getnameinfo.c index 48eb16d8ca..45608dae85 100644 --- a/deps/uv/src/win/getnameinfo.c +++ b/deps/uv/src/win/getnameinfo.c @@ -27,23 +27,31 @@ #include "internal.h" #include "req-inl.h" - -/* getnameinfo worker thread implementation */ -static DWORD WINAPI getnameinfo_thread_proc(void* parameter) { - uv_getnameinfo_t* req = (uv_getnameinfo_t*)parameter; - uv_loop_t* loop = req->loop; +#ifndef GetNameInfo +int WSAAPI GetNameInfoW( + const SOCKADDR *pSockaddr, + socklen_t SockaddrLength, + PWCHAR pNodeBuffer, + DWORD NodeBufferSize, + PWCHAR pServiceBuffer, + DWORD ServiceBufferSize, + INT Flags +); +#endif + +static void uv__getnameinfo_work(struct uv__work* w) { + uv_getnameinfo_t* req; WCHAR host[NI_MAXHOST]; WCHAR service[NI_MAXSERV]; int ret = 0; - assert(req != NULL); - + req = container_of(w, uv_getnameinfo_t, work_req); ret = GetNameInfoW((struct sockaddr*)&req->storage, sizeof(req->storage), host, - sizeof(host), + ARRAY_SIZE(host), service, - sizeof(service), + ARRAY_SIZE(service), req->flags); req->retcode = uv__getaddrinfo_translate_error(ret); @@ -65,30 +73,29 @@ static DWORD WINAPI getnameinfo_thread_proc(void* parameter) { sizeof(req->service), NULL, NULL); - - /* post getnameinfo completed */ - POST_COMPLETION_FOR_REQ(loop, req); - - return 0; } /* * Called from uv_run when complete. */ -void uv_process_getnameinfo_req(uv_loop_t* loop, uv_getnameinfo_t* req) { +static void uv__getnameinfo_done(struct uv__work* w, int status) { + uv_getnameinfo_t* req; char* host; char* service; - if (req->retcode == 0) { + req = container_of(w, uv_getnameinfo_t, work_req); + uv__req_unregister(req->loop, req); + host = service = NULL; + + if (status == UV_ECANCELED) { + assert(req->retcode == 0); + req->retcode = UV_EAI_CANCELED; + } else if (req->retcode == 0) { host = req->host; service = req->service; - } else { - host = NULL; - service = NULL; } - uv__req_unregister(loop, req); req->getnameinfo_cb(req, req->retcode, host, service); } @@ -119,20 +126,18 @@ int uv_getnameinfo(uv_loop_t* loop, } uv_req_init(loop, (uv_req_t*)req); + uv__req_register(loop, req); req->getnameinfo_cb = getnameinfo_cb; req->flags = flags; req->type = UV_GETNAMEINFO; req->loop = loop; + req->retcode = 0; - /* Ask thread to run. Treat this as a long operation. */ - if (QueueUserWorkItem(&getnameinfo_thread_proc, - req, - WT_EXECUTELONGFUNCTION) == 0) { - return uv_translate_sys_error(GetLastError()); - } - - uv__req_register(loop, req); + uv__work_submit(loop, + &req->work_req, + uv__getnameinfo_work, + uv__getnameinfo_done); return 0; } diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index 83c4a66893..9eadb71235 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -31,13 +31,16 @@ #ifdef _MSC_VER # define INLINE __inline +# define UV_THREAD_LOCAL __declspec( thread ) #else # define INLINE inline +# define UV_THREAD_LOCAL __thread #endif #ifdef _DEBUG -extern __declspec( thread ) int uv__crt_assert_enabled; + +extern UV_THREAD_LOCAL int uv__crt_assert_enabled; #define UV_BEGIN_DISABLE_CRT_ASSERT() \ { \ @@ -72,7 +75,6 @@ extern __declspec( thread ) int uv__crt_assert_enabled; /* Used by streams and UDP handles. */ #define UV_HANDLE_READING 0x00000100 #define UV_HANDLE_BOUND 0x00000200 -#define UV_HANDLE_BIND_ERROR 0x00000400 #define UV_HANDLE_LISTENING 0x00000800 #define UV_HANDLE_CONNECTION 0x00001000 #define UV_HANDLE_CONNECTED 0x00002000 @@ -122,6 +124,12 @@ extern __declspec( thread ) int uv__crt_assert_enabled; /* * TCP */ + +typedef struct { + WSAPROTOCOL_INFOW socket_info; + int delayed_error; +} uv__ipc_socket_info_ex; + 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, @@ -140,7 +148,7 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp); void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); -int uv_tcp_import(uv_tcp_t* tcp, WSAPROTOCOL_INFOW* socket_protocol_info, +int uv_tcp_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex, int tcp_connection); int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, @@ -231,7 +239,7 @@ void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle); */ void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle); -DWORD uv_get_poll_timeout(uv_loop_t* loop); +DWORD uv__next_timeout(const uv_loop_t* loop); void uv__time_forward(uv_loop_t* loop, uint64_t msecs); void uv_process_timers(uv_loop_t* loop); @@ -285,29 +293,10 @@ void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle); int uv_translate_sys_error(int sys_errno); -/* - * Getaddrinfo - */ -void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* req); - - -/* -* Getnameinfo -*/ -void uv_process_getnameinfo_req(uv_loop_t* loop, uv_getnameinfo_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); /* diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index 2fcedde369..3bf2a220d0 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -35,10 +35,10 @@ typedef struct uv__ipc_queue_item_s uv__ipc_queue_item_t; struct uv__ipc_queue_item_s { /* - * NOTE: It is important for socket_info to be the first field, + * NOTE: It is important for socket_info_ex to be the first field, * because we will we assigning it to the pending_ipc_info.socket_info */ - WSAPROTOCOL_INFOW socket_info; + uv__ipc_socket_info_ex socket_info_ex; QUEUE member; int tcp_connection; }; @@ -73,7 +73,7 @@ typedef struct { /* IPC frame, which contains an imported TCP socket stream. */ typedef struct { uv_ipc_frame_header_t header; - WSAPROTOCOL_INFOW socket_info; + uv__ipc_socket_info_ex socket_info_ex; } uv_ipc_frame_uv_stream; static void eof_timer_init(uv_pipe_t* pipe); @@ -230,7 +230,7 @@ static int uv_set_pipe_handle(uv_loop_t* loop, NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_MODE_INFORMATION mode_info; - DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + DWORD mode = PIPE_READMODE_BYTE | PIPE_WAIT; DWORD current_mode = 0; DWORD err = 0; @@ -246,11 +246,9 @@ static int uv_set_pipe_handle(uv_loop_t* loop, if (!GetNamedPipeHandleState(pipeHandle, ¤t_mode, NULL, NULL, NULL, NULL, 0)) { return -1; - } else if (current_mode != mode) { + } else if (current_mode & PIPE_NOWAIT) { SetLastError(ERROR_ACCESS_DENIED); return -1; - } else { - duplex_flags &= ~UV_HANDLE_WRITABLE; } } else { /* If this returns ERROR_INVALID_PARAMETER we probably opened @@ -410,7 +408,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { socket = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, - &item->socket_info, + &item->socket_info_ex.socket_info, 0, WSA_FLAG_OVERLAPPED); free(item); @@ -789,7 +787,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { item = QUEUE_DATA(q, uv__ipc_queue_item_t, member); err = uv_tcp_import((uv_tcp_t*)client, - &item->socket_info, + &item->socket_info_ex, item->tcp_connection); if (err != 0) return err; @@ -1134,10 +1132,13 @@ static int uv_pipe_write_impl(uv_loop_t* loop, tcp_send_handle = (uv_tcp_t*)send_handle; err = uv_tcp_duplicate_socket(tcp_send_handle, handle->ipc_pid, - &ipc_frame.socket_info); + &ipc_frame.socket_info_ex.socket_info); if (err) { return err; } + + ipc_frame.socket_info_ex.delayed_error = tcp_send_handle->delayed_error; + ipc_frame.header.flags |= UV_IPC_TCP_SERVER; if (tcp_send_handle->flags & UV_HANDLE_CONNECTION) { @@ -1254,7 +1255,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, } /* Request queued by the kernel. */ - req->queued_bytes = uv_count_bufs(bufs, nbufs); + req->queued_bytes = uv__count_bufs(bufs, nbufs); handle->write_queue_size += req->queued_bytes; } else if (handle->flags & UV_HANDLE_BLOCKING_WRITES) { /* Using overlapped IO, but wait for completion before returning */ @@ -1311,7 +1312,7 @@ static int uv_pipe_write_impl(uv_loop_t* loop, req->queued_bytes = 0; } else { /* Request queued by the kernel. */ - req->queued_bytes = uv_count_bufs(bufs, nbufs); + req->queued_bytes = uv__count_bufs(bufs, nbufs); handle->write_queue_size += req->queued_bytes; } @@ -1397,7 +1398,7 @@ static void uv_pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, void uv__pipe_insert_pending_socket(uv_pipe_t* handle, - WSAPROTOCOL_INFOW* info, + uv__ipc_socket_info_ex* info, int tcp_connection) { uv__ipc_queue_item_t* item; @@ -1405,7 +1406,7 @@ void uv__pipe_insert_pending_socket(uv_pipe_t* handle, if (item == NULL) uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - memcpy(&item->socket_info, info, sizeof(item->socket_info)); + memcpy(&item->socket_info_ex, info, sizeof(item->socket_info_ex)); item->tcp_connection = tcp_connection; QUEUE_INSERT_TAIL(&handle->pending_ipc_info.queue, &item->member); handle->pending_ipc_info.queue_len++; @@ -1471,11 +1472,11 @@ void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, if (ipc_frame.header.flags & UV_IPC_TCP_SERVER) { assert(avail - sizeof(ipc_frame.header) >= - sizeof(ipc_frame.socket_info)); + sizeof(ipc_frame.socket_info_ex)); /* Read the TCP socket info. */ if (!ReadFile(handle->handle, - &ipc_frame.socket_info, + &ipc_frame.socket_info_ex, sizeof(ipc_frame) - sizeof(ipc_frame.header), &bytes, NULL)) { @@ -1489,7 +1490,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, /* Store the pending socket info. */ uv__pipe_insert_pending_socket( handle, - &ipc_frame.socket_info, + &ipc_frame.socket_info_ex, ipc_frame.header.flags & UV_IPC_TCP_CONNECTION); } @@ -1772,7 +1773,34 @@ static void eof_timer_close_cb(uv_handle_t* handle) { int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { HANDLE os_handle = uv__get_osfhandle(file); - DWORD duplex_flags = UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; + NTSTATUS nt_status; + IO_STATUS_BLOCK io_status; + FILE_ACCESS_INFORMATION access; + DWORD duplex_flags = 0; + + /* Determine what kind of permissions we have on this handle. + * Cygwin opens the pipe in message mode, but we can support it, + * just query the access flags and set the stream flags accordingly. + */ + nt_status = pNtQueryInformationFile(os_handle, + &io_status, + &access, + sizeof(access), + FileAccessInformation); + if (nt_status != STATUS_SUCCESS) + return UV_EINVAL; + + if (pipe->ipc) { + if (!(access.AccessFlags & FILE_WRITE_DATA) || + !(access.AccessFlags & FILE_READ_DATA)) { + return UV_EINVAL; + } + } + + if (access.AccessFlags & FILE_WRITE_DATA) + duplex_flags |= UV_HANDLE_WRITABLE; + if (access.AccessFlags & FILE_READ_DATA) + duplex_flags |= UV_HANDLE_READABLE; if (os_handle == INVALID_HANDLE_VALUE || uv_set_pipe_handle(pipe->loop, pipe, os_handle, duplex_flags) == -1) { diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 7a85858c56..40023e5572 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "uv.h" #include "internal.h" @@ -36,14 +38,27 @@ typedef struct env_var { - const char* narrow; - const WCHAR* wide; - size_t len; /* including null or '=' */ - DWORD value_len; - int supplied; + const WCHAR* const wide; + const WCHAR* const wide_eq; + const size_t len; /* including null or '=' */ } env_var_t; -#define E_V(str) { str "=", L##str, sizeof(str), 0, 0 } +#define E_V(str) { L##str, L##str L"=", sizeof(str) } + +static const env_var_t required_vars[] = { /* keep me sorted */ + E_V("HOMEDRIVE"), + E_V("HOMEPATH"), + E_V("LOGONSERVER"), + E_V("PATH"), + E_V("SYSTEMDRIVE"), + E_V("SYSTEMROOT"), + E_V("TEMP"), + E_V("USERDOMAIN"), + E_V("USERNAME"), + E_V("USERPROFILE"), + E_V("WINDIR"), +}; +static size_t n_required_vars = ARRAY_SIZE(required_vars); static HANDLE uv_global_job_handle_; @@ -587,25 +602,56 @@ error: } -/* - * 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 count, - const char* var) { - int i; - for (i = 0; i < count; ++i) { - if (_strnicmp(required[i].narrow, var, required[i].len) == 0) { - required[i].supplied = 1; - return; +int env_strncmp(const wchar_t* a, int na, const wchar_t* b) { + wchar_t* a_eq; + wchar_t* b_eq; + wchar_t* A; + wchar_t* B; + int nb; + int r; + + if (na < 0) { + a_eq = wcschr(a, L'='); + assert(a_eq); + na = (int)(long)(a_eq - a); + } else { + na--; + } + b_eq = wcschr(b, L'='); + assert(b_eq); + nb = b_eq - b; + + A = alloca((na+1) * sizeof(wchar_t)); + B = alloca((nb+1) * sizeof(wchar_t)); + + r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na); + assert(r==na); + A[na] = L'\0'; + r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb); + assert(r==nb); + B[nb] = L'\0'; + + while (1) { + wchar_t AA = *A++; + wchar_t BB = *B++; + if (AA < BB) { + return -1; + } else if (AA > BB) { + return 1; + } else if (!AA && !BB) { + return 0; } } } +static int qsort_wcscmp(const void *a, const void *b) { + wchar_t* astr = *(wchar_t* const*)a; + wchar_t* bstr = *(wchar_t* const*)b; + return env_strncmp(astr, -1, bstr); +} + + /* * The way windows takes environment variables is different than what C does; * Windows wants a contiguous block of null-terminated strings, terminated @@ -616,95 +662,171 @@ static void check_required_vars_contains_var(env_var_t* required, int count, * 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. + * + * Also add variables known to Cygwin to be required for correct + * subprocess operation in many cases: + * https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955 + * */ int make_program_env(char* env_block[], WCHAR** dst_ptr) { WCHAR* dst; WCHAR* ptr; char** env; - size_t env_len = 1; /* room for closing null */ + size_t env_len = 0; int len; size_t i; DWORD var_size; + size_t env_block_count = 1; /* 1 for null-terminator */ + WCHAR* dst_copy; + WCHAR** ptr_copy; + WCHAR** env_copy; + DWORD* required_vars_value_len = alloca(n_required_vars * sizeof(DWORD*)); - env_var_t required_vars[] = { - E_V("SYSTEMROOT"), - E_V("SYSTEMDRIVE"), - E_V("TEMP"), - }; - + /* first pass: determine size in UTF-16 */ for (env = env_block; *env; env++) { int len; - check_required_vars_contains_var(required_vars, - ARRAY_SIZE(required_vars), - *env); - - len = MultiByteToWideChar(CP_UTF8, - 0, - *env, - -1, - NULL, - 0); - if (len <= 0) { - return GetLastError(); + if (strchr(*env, '=')) { + len = MultiByteToWideChar(CP_UTF8, + 0, + *env, + -1, + NULL, + 0); + if (len <= 0) { + return GetLastError(); + } + env_len += len; + env_block_count++; } + } - env_len += len; + /* second pass: copy to UTF-16 environment block */ + dst_copy = _malloca(env_len * sizeof(WCHAR)); + if (!dst_copy) { + return ERROR_OUTOFMEMORY; } + env_copy = alloca(env_block_count * sizeof(WCHAR*)); - for (i = 0; i < ARRAY_SIZE(required_vars); ++i) { - if (!required_vars[i].supplied) { - env_len += required_vars[i].len; + ptr = dst_copy; + ptr_copy = env_copy; + for (env = env_block; *env; env++) { + if (strchr(*env, '=')) { + len = MultiByteToWideChar(CP_UTF8, + 0, + *env, + -1, + ptr, + (int) (env_len - (ptr - dst_copy))); + if (len <= 0) { + DWORD err = GetLastError(); + _freea(dst_copy); + return err; + } + *ptr_copy++ = ptr; + ptr += len; + } + } + *ptr_copy = NULL; + assert(env_len == ptr - dst_copy); + + /* sort our (UTF-16) copy */ + qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp); + + /* third pass: check for required variables */ + for (ptr_copy = env_copy, i = 0; i < n_required_vars; ) { + int cmp; + if (!*ptr_copy) { + cmp = -1; + } else { + cmp = env_strncmp(required_vars[i].wide_eq, + required_vars[i].len, + *ptr_copy); + } + if (cmp < 0) { + /* missing required var */ var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0); - if (var_size == 0) { - return GetLastError(); + required_vars_value_len[i] = var_size; + if (var_size != 0) { + env_len += required_vars[i].len; + env_len += var_size; } - required_vars[i].value_len = var_size; - env_len += var_size; + i++; + } else { + ptr_copy++; + if (cmp == 0) + i++; } } - dst = malloc(env_len * sizeof(WCHAR)); + /* final pass: copy, in sort order, and inserting required variables */ + dst = malloc((1+env_len) * sizeof(WCHAR)); if (!dst) { + _freea(dst_copy); return ERROR_OUTOFMEMORY; } - ptr = dst; - - for (env = env_block; *env; env++, ptr += len) { - len = MultiByteToWideChar(CP_UTF8, - 0, - *env, - -1, - ptr, - (int) (env_len - (ptr - dst))); - if (len <= 0) { - free(dst); - return GetLastError(); + for (ptr = dst, ptr_copy = env_copy, i = 0; + *ptr_copy || i < n_required_vars; + ptr += len) { + int cmp; + if (i >= n_required_vars) { + cmp = 1; + } else if (!*ptr_copy) { + cmp = -1; + } else { + cmp = env_strncmp(required_vars[i].wide_eq, + required_vars[i].len, + *ptr_copy); } - } - - for (i = 0; i < ARRAY_SIZE(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"); + if (cmp < 0) { + /* missing required var */ + len = required_vars_value_len[i]; + if (len) { + wcscpy(ptr, required_vars[i].wide_eq); + ptr += required_vars[i].len; + var_size = GetEnvironmentVariableW(required_vars[i].wide, + ptr, + (int) (env_len - (ptr - dst))); + if (var_size != len-1) { /* race condition? */ + uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); + } } - ptr += required_vars[i].value_len; + i++; + } else { + /* copy var from env_block */ + DWORD r; + len = wcslen(*ptr_copy) + 1; + r = wmemcpy_s(ptr, (env_len - (ptr - dst)), *ptr_copy, len); + assert(!r); + ptr_copy++; + if (cmp == 0) + i++; } } /* Terminate with an extra NULL. */ + assert(env_len == (ptr - dst)); *ptr = L'\0'; + _freea(dst_copy); *dst_ptr = dst; return 0; } +/* + * Attempt to find the value of the PATH environment variable in the child's + * preprocessed environment. + * + * If found, a pointer into `env` is returned. If not found, NULL is returned. + */ +static WCHAR* find_path(WCHAR *env) { + for (; env != NULL && *env != 0; env += wcslen(env) + 1) { + if (wcsncmp(env, L"PATH=", 5) == 0) + return &env[5]; + } + + return NULL; +} /* * Called on Windows thread-pool thread to indicate that @@ -802,7 +924,7 @@ int uv_spawn(uv_loop_t* loop, const uv_process_options_t* options) { int i; int err = 0; - WCHAR* path = NULL; + WCHAR* path = NULL, *alloc_path = NULL; BOOL result; WCHAR* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; @@ -876,7 +998,8 @@ int uv_spawn(uv_loop_t* loop, } /* Get PATH environment variable. */ - { + path = find_path(env); + if (path == NULL) { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); @@ -885,11 +1008,12 @@ int uv_spawn(uv_loop_t* loop, goto done; } - path = (WCHAR*) malloc(path_len * sizeof(WCHAR)); - if (path == NULL) { + alloc_path = (WCHAR*) malloc(path_len * sizeof(WCHAR)); + if (alloc_path == NULL) { err = ERROR_OUTOFMEMORY; goto done; } + path = alloc_path; r = GetEnvironmentVariableW(L"PATH", path, path_len); if (r == 0 || r >= path_len) { @@ -1023,7 +1147,7 @@ int uv_spawn(uv_loop_t* loop, free(arguments); free(cwd); free(env); - free(path); + free(alloc_path); if (process->child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ diff --git a/deps/uv/src/win/req-inl.h b/deps/uv/src/win/req-inl.h index cbc2ba8e18..97342e5c7e 100644 --- a/deps/uv/src/win/req-inl.h +++ b/deps/uv/src/win/req-inl.h @@ -195,26 +195,10 @@ INLINE static void uv_process_reqs(uv_loop_t* loop) { uv_process_poll_req(loop, (uv_poll_t*) req->data, req); break; - case UV_GETADDRINFO: - uv_process_getaddrinfo_req(loop, (uv_getaddrinfo_t*) req); - break; - - case UV_GETNAMEINFO: - uv_process_getnameinfo_req(loop, (uv_getnameinfo_t*)req); - break; - case UV_PROCESS_EXIT: uv_process_proc_exit(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; diff --git a/deps/uv/src/win/stream-inl.h b/deps/uv/src/win/stream-inl.h index e4bf086368..97a6b90b50 100644 --- a/deps/uv/src/win/stream-inl.h +++ b/deps/uv/src/win/stream-inl.h @@ -53,15 +53,4 @@ INLINE static void uv_connection_init(uv_stream_t* handle) { } -INLINE static size_t uv_count_bufs(const uv_buf_t bufs[], unsigned int nbufs) { - unsigned int i; - size_t bytes; - - bytes = 0; - for (i = 0; i < nbufs; i++) - bytes += (size_t) bufs[i].len; - - return bytes; -} - #endif /* UV_WIN_STREAM_INL_H_ */ diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index ccd7a11e2c..a213ad63e7 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -156,6 +156,7 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) { handle->func_acceptex = NULL; handle->func_connectex = NULL; handle->processed_accepts = 0; + handle->delayed_error = 0; return 0; } @@ -235,6 +236,17 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { } +/* Unlike on Unix, here we don't set SO_REUSEADDR, because it doesn't just + * allow binding to addresses that are in use by sockets in TIME_WAIT, it + * effectively allows 'stealing' a port which is in use by another application. + * + * SO_EXCLUSIVEADDRUSE is also not good here because it does cehck all sockets, + * regardless of state, so we'd get an error even if the port is in use by a + * socket in TIME_WAIT state. + * + * See issue #1360. + * + */ static int uv_tcp_try_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, @@ -291,8 +303,7 @@ static int uv_tcp_try_bind(uv_tcp_t* handle, err = WSAGetLastError(); if (err == WSAEADDRINUSE) { /* Some errors are not to be reported until connect() or listen() */ - handle->bind_error = err; - handle->flags |= UV_HANDLE_BIND_ERROR; + handle->delayed_error = err; } else { return err; } @@ -517,8 +528,8 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { return WSAEISCONN; } - if (handle->flags & UV_HANDLE_BIND_ERROR) { - return handle->bind_error; + if (handle->delayed_error) { + return handle->delayed_error; } if (!(handle->flags & UV_HANDLE_BOUND)) { @@ -528,6 +539,8 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { 0); if (err) return err; + if (handle->delayed_error) + return handle->delayed_error; } if (!handle->func_acceptex) { @@ -699,8 +712,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, DWORD bytes; int err; - if (handle->flags & UV_HANDLE_BIND_ERROR) { - return handle->bind_error; + if (handle->delayed_error) { + return handle->delayed_error; } if (!(handle->flags & UV_HANDLE_BOUND)) { @@ -714,6 +727,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, err = uv_tcp_try_bind(handle, bind_addr, addrlen, 0); if (err) return err; + if (handle->delayed_error) + return handle->delayed_error; } if (!handle->func_connectex) { @@ -762,8 +777,8 @@ int uv_tcp_getsockname(const uv_tcp_t* handle, return UV_EINVAL; } - if (handle->flags & UV_HANDLE_BIND_ERROR) { - return uv_translate_sys_error(handle->bind_error); + if (handle->delayed_error) { + return uv_translate_sys_error(handle->delayed_error); } result = getsockname(handle->socket, name, namelen); @@ -784,8 +799,8 @@ int uv_tcp_getpeername(const uv_tcp_t* handle, return UV_EINVAL; } - if (handle->flags & UV_HANDLE_BIND_ERROR) { - return uv_translate_sys_error(handle->bind_error); + if (handle->delayed_error) { + return uv_translate_sys_error(handle->delayed_error); } result = getpeername(handle->socket, name, namelen); @@ -839,7 +854,7 @@ int uv_tcp_write(uv_loop_t* loop, 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, nbufs); + req->queued_bytes = uv__count_bufs(bufs, nbufs); handle->reqs_pending++; handle->write_reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); @@ -1009,8 +1024,12 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, } if (req->cb) { - err = GET_REQ_SOCK_ERROR(req); - req->cb(req, uv_translate_sys_error(err)); + err = uv_translate_sys_error(GET_REQ_SOCK_ERROR(req)); + if (err == UV_ECONNABORTED) { + /* use UV_ECANCELED for consistency with Unix */ + err = UV_ECANCELED; + } + req->cb(req, err); } handle->write_reqs_pending--; @@ -1102,14 +1121,13 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, } -int uv_tcp_import(uv_tcp_t* tcp, WSAPROTOCOL_INFOW* socket_protocol_info, +int uv_tcp_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex, int tcp_connection) { int err; - SOCKET socket = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, - socket_protocol_info, + &socket_info_ex->socket_info, 0, WSA_FLAG_OVERLAPPED); @@ -1126,7 +1144,7 @@ int uv_tcp_import(uv_tcp_t* tcp, WSAPROTOCOL_INFOW* socket_protocol_info, err = uv_tcp_set_socket(tcp->loop, tcp, socket, - socket_protocol_info->iAddressFamily, + socket_info_ex->socket_info.iAddressFamily, 1); if (err) { closesocket(socket); @@ -1141,6 +1159,8 @@ int uv_tcp_import(uv_tcp_t* tcp, WSAPROTOCOL_INFOW* socket_protocol_info, tcp->flags |= UV_HANDLE_BOUND; tcp->flags |= UV_HANDLE_SHARED_TCP_SOCKET; + tcp->delayed_error = socket_info_ex->delayed_error; + tcp->loop->active_tcp_streams++; return 0; } @@ -1201,13 +1221,10 @@ int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, return ERROR_INVALID_PARAMETER; } - /* Report any deferred bind errors now. */ - if (handle->flags & UV_HANDLE_BIND_ERROR) { - return handle->bind_error; - } - - if (listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) { - return WSAGetLastError(); + if (!(handle->delayed_error)) { + if (listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) { + handle->delayed_error = WSAGetLastError(); + } } } } diff --git a/deps/uv/src/win/threadpool.c b/deps/uv/src/win/threadpool.c deleted file mode 100644 index 9539844c66..0000000000 --- a/deps/uv/src/win/threadpool.c +++ /dev/null @@ -1,81 +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 "uv.h" -#include "internal.h" -#include "req-inl.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) { - if (work_cb == NULL) - return UV_EINVAL; - - uv_work_req_init(loop, req, work_cb, after_work_cb); - - if (!QueueUserWorkItem(&uv_work_thread_proc, req, WT_EXECUTELONGFUNCTION)) { - return uv_translate_sys_error(GetLastError()); - } - - uv__req_register(loop, req); - return 0; -} - - -int uv_cancel(uv_req_t* req) { - return UV_ENOSYS; -} - - -void uv_process_work_req(uv_loop_t* loop, uv_work_t* req) { - uv__req_unregister(loop, req); - if(req->after_work_cb) - req->after_work_cb(req, 0); -} diff --git a/deps/uv/src/win/timer.c b/deps/uv/src/win/timer.c index 16a2fc5f5f..c229d4c897 100644 --- a/deps/uv/src/win/timer.c +++ b/deps/uv/src/win/timer.c @@ -36,10 +36,11 @@ void uv_update_time(uv_loop_t* loop) { time.QuadPart = loop->time; - /* GetTickCount() can conceivably wrap around, so when the current tick */ - /* count is lower than the last tick count, we'll assume it has wrapped. */ - /* uv_poll must make sure that the timer can never overflow more than */ - /* once between two subsequent uv_update_time calls. */ + /* GetTickCount() can conceivably wrap around, so when the current tick + * count is lower than the last tick count, we'll assume it has wrapped. + * uv_poll must make sure that the timer can never overflow more than + * once between two subsequent uv_update_time calls. + */ time.LowPart = ticks; if (ticks < loop->last_tick_count) time.HighPart++; @@ -47,13 +48,14 @@ void uv_update_time(uv_loop_t* loop) { /* Remember the last tick count. */ loop->last_tick_count = ticks; - /* The GetTickCount() resolution isn't too good. Sometimes it'll happen */ - /* that GetQueuedCompletionStatus() or GetQueuedCompletionStatusEx() has */ - /* waited for a couple of ms but this is not reflected in the GetTickCount */ - /* result yet. Therefore whenever GetQueuedCompletionStatus times out */ - /* we'll add the number of ms that it has waited to the current loop time. */ - /* When that happened the loop time might be a little ms farther than what */ - /* we've just computed, and we shouldn't update the loop time. */ + /* The GetTickCount() resolution isn't too good. Sometimes it'll happen + * that GetQueuedCompletionStatus() or GetQueuedCompletionStatusEx() has + * waited for a couple of ms but this is not reflected in the GetTickCount + * result yet. Therefore whenever GetQueuedCompletionStatus times out + * we'll add the number of ms that it has waited to the current loop time. + * When that happened the loop time might be a little ms farther than what + * we've just computed, and we shouldn't update the loop time. + */ if (loop->time < time.QuadPart) loop->time = time.QuadPart; } @@ -193,24 +195,26 @@ uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { } -DWORD uv_get_poll_timeout(uv_loop_t* loop) { +DWORD uv__next_timeout(const 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); + /* Check if there are any running timers + * Need to cast away const first, since RB_MIN doesn't know what we are + * going to do with this return value, it can't be marked const + */ + timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)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. */ + /* 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 */ diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index 87e3eb5d8a..6b8297cbd9 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -30,6 +30,10 @@ # include #endif +#ifndef COMMON_LVB_REVERSE_VIDEO +# define COMMON_LVB_REVERSE_VIDEO 0x4000 +#endif + #include "uv.h" #include "internal.h" #include "handle-inl.h" diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index 865890455a..ef63dd73df 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -37,8 +37,9 @@ const unsigned int uv_active_udp_streams_threshold = 0; /* A zero-size buffer for use by uv_udp_read */ static char uv_zero_[] = ""; -int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, - int* namelen) { +int uv_udp_getsockname(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { int result; if (!(handle->flags & UV_HANDLE_BOUND)) { @@ -129,6 +130,8 @@ int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { handle->activecnt = 0; handle->func_wsarecv = WSARecv; handle->func_wsarecvfrom = WSARecvFrom; + handle->send_queue_size = 0; + handle->send_queue_count = 0; uv_req_init(loop, (uv_req_t*) &(handle->recv_req)); handle->recv_req.type = UV_UDP_RECV; @@ -396,12 +399,16 @@ static int uv__send(uv_udp_send_t* req, /* Request completed immediately. */ req->queued_bytes = 0; handle->reqs_pending++; + handle->send_queue_size += req->queued_bytes; + handle->send_queue_count++; REGISTER_HANDLE_REQ(loop, handle, req); 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, nbufs); + req->queued_bytes = uv__count_bufs(bufs, nbufs); handle->reqs_pending++; + handle->send_queue_size += req->queued_bytes; + handle->send_queue_count++; REGISTER_HANDLE_REQ(loop, handle, req); } else { /* Send failed due to an error. */ @@ -524,6 +531,11 @@ void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, assert(handle->type == UV_UDP); + assert(handle->send_queue_size >= req->queued_bytes); + assert(handle->send_queue_count >= 1); + handle->send_queue_size -= req->queued_bytes; + handle->send_queue_count--; + UNREGISTER_HANDLE_REQ(loop, handle, req); if (req->cb) { @@ -860,3 +872,12 @@ int uv__udp_send(uv_udp_send_t* req, return 0; } + + +int uv__udp_try_send(uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr, + unsigned int addrlen) { + return UV_ENOSYS; +} diff --git a/deps/uv/test/run-tests.c b/deps/uv/test/run-tests.c index cd50ee09f3..d8f3cda540 100644 --- a/deps/uv/test/run-tests.c +++ b/deps/uv/test/run-tests.c @@ -39,6 +39,7 @@ int ipc_helper(int listen_after_write); int ipc_helper_tcp_connection(void); int ipc_send_recv_helper(void); +int ipc_helper_bind_twice(void); int stdio_over_pipes_helper(void); static int maybe_run_test(int argc, char **argv); @@ -82,6 +83,10 @@ static int maybe_run_test(int argc, char **argv) { return ipc_helper_tcp_connection(); } + if (strcmp(argv[1], "ipc_helper_bind_twice") == 0) { + return ipc_helper_bind_twice(); + } + if (strcmp(argv[1], "stdio_over_pipes_helper") == 0) { return stdio_over_pipes_helper(); } diff --git a/deps/uv/test/test-barrier.c b/deps/uv/test/test-barrier.c index 25a55d6cfe..dfd2dbdef1 100644 --- a/deps/uv/test/test-barrier.c +++ b/deps/uv/test/test-barrier.c @@ -41,10 +41,6 @@ static void worker(void* arg) { uv_sleep(c->delay); c->worker_barrier_wait_rval = uv_barrier_wait(&c->barrier); - if (c->worker_barrier_wait_rval == 1) { - uv_barrier_destroy(&c->barrier); - ASSERT(c->main_barrier_wait_rval == 0); - } } @@ -53,21 +49,16 @@ TEST_IMPL(barrier_1) { worker_config wc; memset(&wc, 0, sizeof(wc)); - wc.main_barrier_wait_rval = -1; - wc.worker_barrier_wait_rval = -1; ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); uv_sleep(100); - wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); - if (wc.main_barrier_wait_rval == 1) { - uv_barrier_destroy(&wc.barrier); - ASSERT(wc.worker_barrier_wait_rval == 0); - } ASSERT(0 == uv_thread_join(&thread)); + uv_barrier_destroy(&wc.barrier); + ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; @@ -80,19 +71,15 @@ TEST_IMPL(barrier_2) { memset(&wc, 0, sizeof(wc)); wc.delay = 100; - wc.main_barrier_wait_rval = -1; - wc.worker_barrier_wait_rval = -1; ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); - if (wc.main_barrier_wait_rval == 1) { - uv_barrier_destroy(&wc.barrier); - ASSERT(wc.worker_barrier_wait_rval == 0); - } ASSERT(0 == uv_thread_join(&thread)); + uv_barrier_destroy(&wc.barrier); + ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; @@ -104,19 +91,15 @@ TEST_IMPL(barrier_3) { worker_config wc; memset(&wc, 0, sizeof(wc)); - wc.main_barrier_wait_rval = -1; - wc.worker_barrier_wait_rval = -1; ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); - if (wc.main_barrier_wait_rval == 1) { - uv_barrier_destroy(&wc.barrier); - ASSERT(wc.worker_barrier_wait_rval == 0); - } ASSERT(0 == uv_thread_join(&thread)); + uv_barrier_destroy(&wc.barrier); + ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 40c7726cad..4c6ccfab2c 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -65,6 +65,7 @@ static int read_cb_count; static int write_cb_count; static int unlink_cb_count; static int mkdir_cb_count; +static int mkdtemp_cb_count; static int rmdir_cb_count; static int readdir_cb_count; static int stat_cb_count; @@ -93,6 +94,8 @@ 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 mkdtemp_req1; +static uv_fs_t mkdtemp_req2; static uv_fs_t rmdir_req; static uv_fs_t readdir_req; static uv_fs_t stat_req; @@ -376,6 +379,32 @@ static void mkdir_cb(uv_fs_t* req) { } +static void check_mkdtemp_result(uv_fs_t* req) { + int r; + + ASSERT(req->fs_type == UV_FS_MKDTEMP); + ASSERT(req->result == 0); + ASSERT(req->path); + ASSERT(strlen(req->path) == 15); + ASSERT(memcmp(req->path, "test_dir_", 9) == 0); + ASSERT(memcmp(req->path + 9, "XXXXXX", 6) != 0); + check_permission(req->path, 0700); + + /* Check if req->path is actually a directory */ + r = uv_fs_stat(uv_default_loop(), &stat_req, req->path, NULL); + ASSERT(r == 0); + ASSERT(((uv_stat_t*)stat_req.ptr)->st_mode & S_IFDIR); + uv_fs_req_cleanup(&stat_req); +} + + +static void mkdtemp_cb(uv_fs_t* req) { + ASSERT(req == &mkdtemp_req1); + check_mkdtemp_result(req); + mkdtemp_cb_count++; +} + + static void rmdir_cb(uv_fs_t* req) { ASSERT(req == &rmdir_req); ASSERT(req->fs_type == UV_FS_RMDIR); @@ -927,6 +956,37 @@ TEST_IMPL(fs_async_sendfile) { } +TEST_IMPL(fs_mkdtemp) { + int r; + const char* path_template = "test_dir_XXXXXX"; + + loop = uv_default_loop(); + + r = uv_fs_mkdtemp(loop, &mkdtemp_req1, path_template, mkdtemp_cb); + ASSERT(r == 0); + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(mkdtemp_cb_count == 1); + + /* sync mkdtemp */ + r = uv_fs_mkdtemp(loop, &mkdtemp_req2, path_template, NULL); + ASSERT(r == 0); + check_mkdtemp_result(&mkdtemp_req2); + + /* mkdtemp return different values on subsequent calls */ + ASSERT(strcmp(mkdtemp_req1.path, mkdtemp_req2.path) != 0); + + /* Cleanup */ + rmdir(mkdtemp_req1.path); + rmdir(mkdtemp_req2.path); + uv_fs_req_cleanup(&mkdtemp_req1); + uv_fs_req_cleanup(&mkdtemp_req2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + TEST_IMPL(fs_fstat) { int r; uv_fs_t req; @@ -985,6 +1045,13 @@ TEST_IMPL(fs_fstat) { ASSERT(s->st_birthtim.tv_nsec == t.st_birthtimespec.tv_nsec); ASSERT(s->st_flags == t.st_flags); ASSERT(s->st_gen == t.st_gen); +#elif defined(_AIX) + ASSERT(s->st_atim.tv_sec == t.st_atime); + ASSERT(s->st_atim.tv_nsec == 0); + ASSERT(s->st_mtim.tv_sec == t.st_mtime); + ASSERT(s->st_mtim.tv_nsec == 0); + ASSERT(s->st_ctim.tv_sec == t.st_ctime); + ASSERT(s->st_ctim.tv_nsec == 0); #elif defined(__sun) || \ defined(_BSD_SOURCE) || \ defined(_SVID_SOURCE) || \ diff --git a/deps/uv/test/test-getsockname.c b/deps/uv/test/test-getsockname.c index a67d967f0b..565c17fe50 100644 --- a/deps/uv/test/test-getsockname.c +++ b/deps/uv/test/test-getsockname.c @@ -353,6 +353,9 @@ TEST_IMPL(getsockname_udp) { ASSERT(getsocknamecount == 2); + ASSERT(udp.send_queue_size == 0); + ASSERT(udpServer.send_queue_size == 0); + MAKE_VALGRIND_HAPPY(); return 0; } diff --git a/deps/uv/test/test-ipc.c b/deps/uv/test/test-ipc.c index 61b649b66c..ed8c4dd7f0 100644 --- a/deps/uv/test/test-ipc.c +++ b/deps/uv/test/test-ipc.c @@ -27,6 +27,7 @@ static uv_pipe_t channel; static uv_tcp_t tcp_server; +static uv_tcp_t tcp_server2; static uv_tcp_t tcp_connection; static int exit_cb_called; @@ -38,8 +39,6 @@ static int local_conn_accepted; static int remote_conn_accepted; static int tcp_server_listening; static uv_write_t write_req; -static uv_pipe_t channel; -static uv_tcp_t tcp_server; static uv_write_t conn_notify_req; static int close_cb_called; static int connection_accepted; @@ -205,6 +204,71 @@ static void on_read(uv_stream_t* handle, free(buf->base); } +#ifdef _WIN32 +static void on_read_listen_after_bound_twice(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + int r; + uv_pipe_t* pipe; + uv_handle_type pending; + + pipe = (uv_pipe_t*) handle; + + if (nread == 0) { + /* Everything OK, but nothing read. */ + free(buf->base); + return; + } + + if (nread < 0) { + if (nread == UV_EOF) { + free(buf->base); + return; + } + + printf("error recving on channel: %s\n", uv_strerror(nread)); + abort(); + } + + fprintf(stderr, "got %d bytes\n", (int)nread); + + ASSERT(uv_pipe_pending_count(pipe) > 0); + pending = uv_pipe_pending_type(pipe); + ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); + read_cb_called++; + + if (read_cb_called == 1) { + /* Accept the first TCP server, and start listening on it. */ + ASSERT(pending == UV_TCP); + r = uv_tcp_init(uv_default_loop(), &tcp_server); + ASSERT(r == 0); + + r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, 12, on_connection); + ASSERT(r == 0); + } else if (read_cb_called == 2) { + /* Accept the second TCP server, and start listening on it. */ + ASSERT(pending == UV_TCP); + r = uv_tcp_init(uv_default_loop(), &tcp_server2); + ASSERT(r == 0); + + r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server2); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server2, 12, on_connection); + ASSERT(r == UV_EADDRINUSE); + + uv_close((uv_handle_t*)&tcp_server, NULL); + uv_close((uv_handle_t*)&tcp_server2, NULL); + ASSERT(0 == uv_pipe_pending_count(pipe)); + uv_close((uv_handle_t*)&channel, NULL); + } + + free(buf->base); +} +#endif void spawn_helper(uv_pipe_t* channel, uv_process_t* process, @@ -424,6 +488,13 @@ TEST_IMPL(listen_no_simultaneous_accepts) { MAKE_VALGRIND_HAPPY(); return 0; } + +TEST_IMPL(ipc_listen_after_bind_twice) { + int r = run_ipc_test("ipc_helper_bind_twice", on_read_listen_after_bound_twice); + ASSERT(read_cb_called == 2); + ASSERT(exit_cb_called == 1); + return r; +} #endif @@ -608,7 +679,7 @@ int ipc_helper(int listen_after_write) { int ipc_helper_tcp_connection(void) { /* - * This is launched from test-ipc.c. stdin is a duplex channel that we + * This is launched from test-ipc.c. stdin is a duplex channel * over which a handle will be transmitted. */ @@ -657,3 +728,51 @@ int ipc_helper_tcp_connection(void) { MAKE_VALGRIND_HAPPY(); return 0; } + +int ipc_helper_bind_twice(void) { + /* + * This is launched from test-ipc.c. stdin is a duplex channel + * over which two handles will be transmitted. + */ + struct sockaddr_in addr; + uv_write_t write_req; + uv_write_t write_req2; + int r; + uv_buf_t buf; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_pipe_init(uv_default_loop(), &channel, 1); + ASSERT(r == 0); + + uv_pipe_open(&channel, 0); + + ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); + ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); + ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + + buf = uv_buf_init("hello\n", 6); + + r = uv_tcp_init(uv_default_loop(), &tcp_server); + ASSERT(r == 0); + r = uv_tcp_init(uv_default_loop(), &tcp_server2); + ASSERT(r == 0); + + r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + r = uv_tcp_bind(&tcp_server2, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, + (uv_stream_t*)&tcp_server, NULL); + ASSERT(r == 0); + r = uv_write2(&write_req2, (uv_stream_t*)&channel, &buf, 1, + (uv_stream_t*)&tcp_server2, NULL); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 15c2e4ed38..6dbe22307e 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -28,6 +28,7 @@ TEST_DECLARE (loop_alive) TEST_DECLARE (loop_close) TEST_DECLARE (loop_stop) TEST_DECLARE (loop_update_time) +TEST_DECLARE (loop_backend_timeout) TEST_DECLARE (barrier_1) TEST_DECLARE (barrier_2) TEST_DECLARE (barrier_3) @@ -56,6 +57,7 @@ TEST_DECLARE (delayed_accept) TEST_DECLARE (multiple_listen) TEST_DECLARE (tcp_writealot) TEST_DECLARE (tcp_try_write) +TEST_DECLARE (tcp_write_queue_order) TEST_DECLARE (tcp_open) TEST_DECLARE (tcp_connect_error_after_write) TEST_DECLARE (tcp_shutdown_after_write) @@ -71,7 +73,9 @@ TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) +#ifndef _WIN32 TEST_DECLARE (tcp_close_accept) +#endif TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) @@ -84,6 +88,7 @@ TEST_DECLARE (tcp_bind6_localhost_ok) TEST_DECLARE (udp_bind) TEST_DECLARE (udp_bind_reuseaddr) TEST_DECLARE (udp_send_and_recv) +TEST_DECLARE (udp_send_immediate) TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_join6) TEST_DECLARE (udp_multicast_ttl) @@ -95,6 +100,7 @@ TEST_DECLARE (udp_ipv6_only) TEST_DECLARE (udp_options) TEST_DECLARE (udp_no_autobind) TEST_DECLARE (udp_open) +TEST_DECLARE (udp_try_send) TEST_DECLARE (pipe_bind_error_addrinuse) TEST_DECLARE (pipe_bind_error_addrnotavail) TEST_DECLARE (pipe_bind_error_inval) @@ -186,6 +192,7 @@ TEST_DECLARE (spawn_stdout_to_file) TEST_DECLARE (spawn_stdout_and_stderr_to_file) TEST_DECLARE (spawn_auto_unref) TEST_DECLARE (spawn_closed_process_io) +TEST_DECLARE (spawn_reads_child_path) TEST_DECLARE (fs_poll) TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (kill) @@ -197,6 +204,7 @@ TEST_DECLARE (fs_file_sync) TEST_DECLARE (fs_file_write_null_buffer) TEST_DECLARE (fs_async_dir) TEST_DECLARE (fs_async_sendfile) +TEST_DECLARE (fs_mkdtemp) TEST_DECLARE (fs_fstat) TEST_DECLARE (fs_chmod) TEST_DECLARE (fs_chown) @@ -254,6 +262,7 @@ TEST_DECLARE (listen_with_simultaneous_accepts) TEST_DECLARE (listen_no_simultaneous_accepts) TEST_DECLARE (fs_stat_root) TEST_DECLARE (spawn_with_an_odd_path) +TEST_DECLARE (ipc_listen_after_bind_twice) #else TEST_DECLARE (emfile) TEST_DECLARE (close_fd) @@ -286,6 +295,7 @@ TASK_LIST_START TEST_ENTRY (loop_close) TEST_ENTRY (loop_stop) TEST_ENTRY (loop_update_time) + TEST_ENTRY (loop_backend_timeout) TEST_ENTRY (barrier_1) TEST_ENTRY (barrier_2) TEST_ENTRY (barrier_3) @@ -330,6 +340,8 @@ TASK_LIST_START TEST_ENTRY (tcp_try_write) + TEST_ENTRY (tcp_write_queue_order) + TEST_ENTRY (tcp_open) TEST_HELPER (tcp_open, tcp4_echo_server) @@ -349,7 +361,9 @@ TASK_LIST_START TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) +#ifndef _WIN32 TEST_ENTRY (tcp_close_accept) +#endif TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) @@ -366,6 +380,7 @@ TASK_LIST_START TEST_ENTRY (udp_bind) TEST_ENTRY (udp_bind_reuseaddr) TEST_ENTRY (udp_send_and_recv) + TEST_ENTRY (udp_send_immediate) TEST_ENTRY (udp_dgram_too_big) TEST_ENTRY (udp_dual_stack) TEST_ENTRY (udp_ipv6_only) @@ -376,6 +391,7 @@ TASK_LIST_START TEST_ENTRY (udp_multicast_join) TEST_ENTRY (udp_multicast_join6) TEST_ENTRY (udp_multicast_ttl) + TEST_ENTRY (udp_try_send) TEST_ENTRY (udp_open) TEST_HELPER (udp_open, udp4_echo_server) @@ -505,6 +521,7 @@ TASK_LIST_START TEST_ENTRY (spawn_stdout_and_stderr_to_file) TEST_ENTRY (spawn_auto_unref) TEST_ENTRY (spawn_closed_process_io) + TEST_ENTRY (spawn_reads_child_path) TEST_ENTRY (fs_poll) TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (kill) @@ -518,6 +535,7 @@ TASK_LIST_START TEST_ENTRY (listen_no_simultaneous_accepts) TEST_ENTRY (fs_stat_root) TEST_ENTRY (spawn_with_an_odd_path) + TEST_ENTRY (ipc_listen_after_bind_twice) #else TEST_ENTRY (emfile) TEST_ENTRY (close_fd) @@ -541,6 +559,7 @@ TASK_LIST_START TEST_ENTRY (fs_file_write_null_buffer) TEST_ENTRY (fs_async_dir) TEST_ENTRY (fs_async_sendfile) + TEST_ENTRY (fs_mkdtemp) TEST_ENTRY (fs_fstat) TEST_ENTRY (fs_chmod) TEST_ENTRY (fs_chown) diff --git a/deps/uv/test/test-loop-time.c b/deps/uv/test/test-loop-time.c index 49dc79b2c3..a2db42ccee 100644 --- a/deps/uv/test/test-loop-time.c +++ b/deps/uv/test/test-loop-time.c @@ -30,5 +30,34 @@ TEST_IMPL(loop_update_time) { while (uv_now(uv_default_loop()) - start < 1000) ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + MAKE_VALGRIND_HAPPY(); + return 0; +} + +static void cb(uv_timer_t* timer) { + uv_close((uv_handle_t*)timer, NULL); +} + +TEST_IMPL(loop_backend_timeout) { + uv_loop_t *loop = uv_default_loop(); + uv_timer_t timer; + int r; + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + ASSERT(!uv_loop_alive(loop)); + ASSERT(uv_backend_timeout(loop) == 0); + + r = uv_timer_start(&timer, cb, 1000, 0); /* 1 sec */ + ASSERT(r == 0); + ASSERT(uv_backend_timeout(loop) > 100); /* 0.1 sec */ + ASSERT(uv_backend_timeout(loop) <= 1000); /* 1 sec */ + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(uv_backend_timeout(loop) == 0); + + MAKE_VALGRIND_HAPPY(); return 0; } diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index c75f1ca2fd..57f0862f94 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -31,6 +31,7 @@ # include # endif # include +# include #else # include #endif @@ -897,45 +898,110 @@ TEST_IMPL(environment_creation) { "SYSTEM=ROOT", /* substring of a supplied var name */ "SYSTEMROOTED=OMG", /* supplied var name is a substring */ "TEMP=C:\\Temp", + "INVALID", "BAZ=QUX", + "B_Z=QUX", + "B\xe2\x82\xacZ=QUX", + "B\xf0\x90\x80\x82Z=QUX", + "B\xef\xbd\xa1Z=QUX", + "B\xf0\xa3\x91\x96Z=QUX", + "BAZ", /* repeat, invalid variable */ NULL }; - - WCHAR expected[512]; - WCHAR* ptr = expected; + WCHAR* wenvironment[] = { + L"BAZ=QUX", + L"B_Z=QUX", + L"B\x20acZ=QUX", + L"B\xd800\xdc02Z=QUX", + L"B\xd84d\xdc56Z=QUX", + L"B\xff61Z=QUX", + L"FOO=BAR", + L"SYSTEM=ROOT", /* substring of a supplied var name */ + L"SYSTEMROOTED=OMG", /* supplied var name is a substring */ + L"TEMP=C:\\Temp", + }; + WCHAR* from_env[] = { + /* list should be kept in sync with list + * in process.c, minus variables in wenvironment */ + L"HOMEDRIVE", + L"HOMEPATH", + L"LOGONSERVER", + L"PATH", + L"USERDOMAIN", + L"USERNAME", + L"USERPROFILE", + L"SYSTEMDRIVE", + L"SYSTEMROOT", + L"WINDIR", + /* test for behavior in the absence of a + * required-environment variable: */ + L"ZTHIS_ENV_VARIABLE_DOES_NOT_EXIST", + }; + int found_in_loc_env[ARRAY_SIZE(wenvironment)] = {0}; + int found_in_usr_env[ARRAY_SIZE(from_env)] = {0}; + WCHAR *expected[ARRAY_SIZE(from_env)]; int result; WCHAR* str; + WCHAR* prev; WCHAR* env; - for (i = 0; i < sizeof(environment) / sizeof(environment[0]) - 1; i++) { - ptr += uv_utf8_to_utf16(environment[i], - ptr, - expected + sizeof(expected) - ptr); + for (i = 0; i < ARRAY_SIZE(from_env); i++) { + /* copy expected additions to environment locally */ + size_t len = GetEnvironmentVariableW(from_env[i], NULL, 0); + if (len == 0) { + found_in_usr_env[i] = 1; + str = malloc(1 * sizeof(WCHAR)); + *str = 0; + expected[i] = str; + } else { + size_t name_len = wcslen(from_env[i]); + str = malloc((name_len+1+len) * sizeof(WCHAR)); + wmemcpy(str, from_env[i], name_len); + expected[i] = str; + str += name_len; + *str++ = L'='; + GetEnvironmentVariableW(from_env[i], str, len); + } } - memcpy(ptr, L"SYSTEMROOT=", sizeof(L"SYSTEMROOT=")); - ptr += sizeof(L"SYSTEMROOT=")/sizeof(WCHAR) - 1; - ptr += GetEnvironmentVariableW(L"SYSTEMROOT", - ptr, - expected + sizeof(expected) - ptr); - ++ptr; - - memcpy(ptr, L"SYSTEMDRIVE=", sizeof(L"SYSTEMDRIVE=")); - ptr += sizeof(L"SYSTEMDRIVE=")/sizeof(WCHAR) - 1; - ptr += GetEnvironmentVariableW(L"SYSTEMDRIVE", - ptr, - expected + sizeof(expected) - ptr); - ++ptr; - *ptr = '\0'; - result = make_program_env(environment, &env); ASSERT(result == 0); - for (str = env; *str; str += wcslen(str) + 1) { - wprintf(L"%s\n", str); + for (str = env, prev = NULL; *str; prev = str, str += wcslen(str) + 1) { + int found = 0; +#if 0 + _cputws(str); + putchar('\n'); +#endif + for (i = 0; i < ARRAY_SIZE(wenvironment) && !found; i++) { + if (!wcscmp(str, wenvironment[i])) { + ASSERT(!found_in_loc_env[i]); + found_in_loc_env[i] = 1; + found = 1; + } + } + for (i = 0; i < ARRAY_SIZE(expected) && !found; i++) { + if (!wcscmp(str, expected[i])) { + ASSERT(!found_in_usr_env[i]); + found_in_usr_env[i] = 1; + found = 1; + } + } + if (prev) { /* verify sort order -- requires Vista */ +#if _WIN32_WINNT >= 0x0600 + ASSERT(CompareStringOrdinal(prev, -1, str, -1, TRUE) == 1); +#endif + } + ASSERT(found); /* verify that we expected this variable */ } - ASSERT(wcscmp(expected, env) == 0); + /* verify that we found all expected variables */ + for (i = 0; i < ARRAY_SIZE(wenvironment); i++) { + ASSERT(found_in_loc_env[i]); + } + for (i = 0; i < ARRAY_SIZE(expected); i++) { + ASSERT(found_in_usr_env[i]); + } return 0; } @@ -1225,3 +1291,38 @@ TEST_IMPL(closed_fd_events) { return 0; } #endif /* !_WIN32 */ + +TEST_IMPL(spawn_reads_child_path) { + int r; + int len; + char path[1024]; + char *env[2] = {path, NULL}; + + /* Set up the process, but make sure that the file to run is relative and */ + /* requires a lookup into PATH */ + init_process_options("spawn_helper1", exit_cb); + options.file = "run-tests"; + args[0] = "run-tests"; + + /* Set up the PATH env variable */ + for (len = strlen(exepath); + exepath[len - 1] != '/' && exepath[len - 1] != '\\'; + len--); + exepath[len] = 0; + strcpy(path, "PATH="); + strcpy(path + 5, exepath); + + options.env = env; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-tcp-close-accept.c b/deps/uv/test/test-tcp-close-accept.c index 10f9d91964..5517aaf99e 100644 --- a/deps/uv/test/test-tcp-close-accept.c +++ b/deps/uv/test/test-tcp-close-accept.c @@ -19,6 +19,9 @@ * IN THE SOFTWARE. */ +/* this test is Unix only */ +#ifndef _WIN32 + #include "uv.h" #include "task.h" @@ -181,3 +184,5 @@ TEST_IMPL(tcp_close_accept) { MAKE_VALGRIND_HAPPY(); return 0; } + +#endif /* !_WIN32 */ diff --git a/deps/uv/test/test-tcp-try-write.c b/deps/uv/test/test-tcp-try-write.c index 00341e4169..baff6cf36c 100644 --- a/deps/uv/test/test-tcp-try-write.c +++ b/deps/uv/test/test-tcp-try-write.c @@ -54,21 +54,19 @@ static void close_cb(uv_handle_t* handle) { static void connect_cb(uv_connect_t* req, int status) { - static char zeroes[1024]; int r; uv_buf_t buf; ASSERT(status == 0); connect_cb_called++; do { - buf = uv_buf_init(zeroes, sizeof(zeroes)); + buf = uv_buf_init("PING", 4); r = uv_try_write((uv_stream_t*) &client, &buf, 1); - ASSERT(r >= 0); - bytes_written += r; - - /* Partial write */ - if (r != (int) sizeof(zeroes)) + ASSERT(r > 0 || r == UV_EAGAIN); + if (r > 0) { + bytes_written += r; break; + } } while (1); uv_close((uv_handle_t*) &client, close_cb); } diff --git a/deps/uv/test/test-tcp-write-queue-order.c b/deps/uv/test/test-tcp-write-queue-order.c new file mode 100644 index 0000000000..18e1f192b6 --- /dev/null +++ b/deps/uv/test/test-tcp-write-queue-order.c @@ -0,0 +1,137 @@ +/* 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 "task.h" + +#define REQ_COUNT 100000 + +static uv_timer_t timer; +static uv_tcp_t server; +static uv_tcp_t client; +static uv_tcp_t incoming; +static int connect_cb_called; +static int close_cb_called; +static int connection_cb_called; +static int write_callbacks; +static int write_cancelled_callbacks; +static int write_error_callbacks; + +static uv_write_t write_requests[REQ_COUNT]; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +void timer_cb(uv_timer_t* handle) { + uv_close((uv_handle_t*) &client, close_cb); + uv_close((uv_handle_t*) &server, close_cb); + uv_close((uv_handle_t*) &incoming, close_cb); +} + +void write_cb(uv_write_t* req, int status) { + if (status == 0) + write_callbacks++; + else if (status == UV_ECANCELED) + write_cancelled_callbacks++; + else + write_error_callbacks++; +} + +static void connect_cb(uv_connect_t* req, int status) { + static char base[1024]; + int r; + int i; + uv_buf_t buf; + + ASSERT(status == 0); + connect_cb_called++; + + buf = uv_buf_init(base, sizeof(base)); + + for (i = 0; i < REQ_COUNT; i++) { + r = uv_write(&write_requests[i], + req->handle, + &buf, + 1, + write_cb); + ASSERT(r == 0); + } +} + + +static void connection_cb(uv_stream_t* tcp, int status) { + ASSERT(status == 0); + + ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); + ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); + + connection_cb_called++; +} + + +static void start_server(void) { + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); + ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); +} + + +TEST_IMPL(tcp_write_queue_order) { + uv_connect_t connect_req; + struct sockaddr_in addr; + + start_server(); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); + ASSERT(0 == uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); + + ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_cb, 100, 0)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(connect_cb_called == 1); + ASSERT(connection_cb_called == 1); + ASSERT(write_callbacks > 0); + ASSERT(write_cancelled_callbacks > 0); + ASSERT(write_callbacks + + write_error_callbacks + + write_cancelled_callbacks == REQ_COUNT); + ASSERT(close_cb_called == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-udp-multicast-interface.c b/deps/uv/test/test-udp-multicast-interface.c index 643df31802..f0679c578e 100644 --- a/deps/uv/test/test-udp-multicast-interface.c +++ b/deps/uv/test/test-udp-multicast-interface.c @@ -91,6 +91,9 @@ TEST_IMPL(udp_multicast_interface) { ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 1); + ASSERT(client.send_queue_size == 0); + ASSERT(server.send_queue_size == 0); + MAKE_VALGRIND_HAPPY(); return 0; } diff --git a/deps/uv/test/test-udp-open.c b/deps/uv/test/test-udp-open.c index 9a97303f12..b2b6117784 100644 --- a/deps/uv/test/test-udp-open.c +++ b/deps/uv/test/test-udp-open.c @@ -159,6 +159,8 @@ TEST_IMPL(udp_open) { ASSERT(send_cb_called == 1); ASSERT(close_cb_called == 1); + ASSERT(client.send_queue_size == 0); + MAKE_VALGRIND_HAPPY(); return 0; } diff --git a/deps/uv/test/test-udp-send-and-recv.c b/deps/uv/test/test-udp-send-and-recv.c index 3020ded7bf..633a16727b 100644 --- a/deps/uv/test/test-udp-send-and-recv.c +++ b/deps/uv/test/test-udp-send-and-recv.c @@ -206,6 +206,9 @@ TEST_IMPL(udp_send_and_recv) { ASSERT(sv_recv_cb_called == 1); ASSERT(close_cb_called == 2); + ASSERT(client.send_queue_size == 0); + ASSERT(server.send_queue_size == 0); + MAKE_VALGRIND_HAPPY(); return 0; } diff --git a/deps/uv/test/test-udp-send-immediate.c b/deps/uv/test/test-udp-send-immediate.c new file mode 100644 index 0000000000..0999f6b342 --- /dev/null +++ b/deps/uv/test/test-udp-send-immediate.c @@ -0,0 +1,148 @@ +/* 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 sv_recv_cb_called; +static int close_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(1 == uv_is_closing(handle)); + close_cb_called++; +} + + +static void cl_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + cl_send_cb_called++; +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + 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", rcvbuf->base, nread) == 0 || + memcmp("PANG", rcvbuf->base, nread) == 0); + + if (++sv_recv_cb_called == 2) { + uv_close((uv_handle_t*) &server, close_cb); + uv_close((uv_handle_t*) &client, close_cb); + } +} + + +TEST_IMPL(udp_send_immediate) { + struct sockaddr_in addr; + uv_udp_send_t req1, req2; + uv_buf_t buf; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + /* client sends "PING", then "PANG" */ + buf = uv_buf_init("PING", 4); + + r = uv_udp_send(&req1, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + cl_send_cb); + ASSERT(r == 0); + + buf = uv_buf_init("PANG", 4); + + r = uv_udp_send(&req2, + &client, + &buf, + 1, + (const struct sockaddr*) &addr, + cl_send_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(cl_send_cb_called == 2); + ASSERT(sv_recv_cb_called == 2); + ASSERT(close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-udp-try-send.c b/deps/uv/test/test-udp-try-send.c new file mode 100644 index 0000000000..7b6de36548 --- /dev/null +++ b/deps/uv/test/test-udp-try-send.c @@ -0,0 +1,133 @@ +/* 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 + +#ifdef _WIN32 + +TEST_IMPL(udp_try_send) { + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#else /* !_WIN32 */ + +#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 sv_recv_cb_called; + +static int close_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(uv_is_closing(handle)); + close_cb_called++; +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + ASSERT(nread > 0); + + if (nread == 0) { + ASSERT(addr == NULL); + return; + } + + ASSERT(nread == 4); + ASSERT(addr != NULL); + + ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0); + uv_close((uv_handle_t*) handle, close_cb); + uv_close((uv_handle_t*) &client, close_cb); + + sv_recv_cb_called++; +} + + +TEST_IMPL(udp_try_send) { + struct sockaddr_in addr; + static char buffer[64 * 1024]; + uv_buf_t buf; + int r; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT(r == 0); + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + buf = uv_buf_init(buffer, sizeof(buffer)); + r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); + ASSERT(r == UV_EMSGSIZE); + + buf = uv_buf_init("EXIT", 4); + r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); + ASSERT(r == 4); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 2); + ASSERT(sv_recv_cb_called == 1); + + ASSERT(client.send_queue_size == 0); + ASSERT(server.send_queue_size == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* !_WIN32 */ diff --git a/deps/uv/test/test-watcher-cross-stop.c b/deps/uv/test/test-watcher-cross-stop.c index 9284a1291f..bf765cb00c 100644 --- a/deps/uv/test/test-watcher-cross-stop.c +++ b/deps/uv/test/test-watcher-cross-stop.c @@ -91,7 +91,7 @@ TEST_IMPL(watcher_cross_stop) { for (i = 0; i < ARRAY_SIZE(sockets); i++) uv_close((uv_handle_t*) &sockets[i], close_cb); - ASSERT(0 < recv_cb_called && recv_cb_called <= ARRAY_SIZE(sockets)); + ASSERT(recv_cb_called > 0); ASSERT(ARRAY_SIZE(sockets) == send_cb_called); uv_run(loop, UV_RUN_DEFAULT); diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 147fc06c3a..5b4d69a924 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -61,11 +61,13 @@ 'include/uv.h', 'include/tree.h', 'include/uv-errno.h', + 'include/uv-threadpool.h', 'include/uv-version.h', 'src/fs-poll.c', 'src/heap-inl.h', 'src/inet.c', 'src/queue.h', + 'src/threadpool.c', 'src/uv-common.c', 'src/uv-common.h', 'src/version.c' @@ -103,7 +105,6 @@ 'src/win/stream-inl.h', 'src/win/tcp.c', 'src/win/tty.c', - 'src/win/threadpool.c', 'src/win/timer.c', 'src/win/udp.c', 'src/win/util.c', @@ -114,11 +115,11 @@ ], 'link_settings': { 'libraries': [ - '-ladvapi32.lib', - '-liphlpapi.lib', - '-lpsapi.lib', - '-lshell32.lib', - '-lws2_32.lib' + '-ladvapi32', + '-liphlpapi', + '-lpsapi', + '-lshell32', + '-lws2_32' ], }, }, { # Not Windows i.e. POSIX @@ -136,6 +137,7 @@ 'include/uv-sunos.h', 'include/uv-darwin.h', 'include/uv-bsd.h', + 'include/uv-aix.h', 'src/unix/async.c', 'src/unix/atomic-ops.h', 'src/unix/core.c', @@ -154,7 +156,6 @@ 'src/unix/stream.c', 'src/unix/tcp.c', 'src/unix/thread.c', - 'src/unix/threadpool.c', 'src/unix/timer.c', 'src/unix/tty.c', 'src/unix/udp.c', @@ -245,6 +246,7 @@ 'defines': [ '_ALL_SOURCE', '_XOPEN_SOURCE=500', + '_LINUX_SOURCE_COMPAT', ], 'link_settings': { 'libraries': [ @@ -378,6 +380,7 @@ 'test/test-tcp-try-write.c', 'test/test-tcp-unexpected-read.c', 'test/test-tcp-read-stop.c', + 'test/test-tcp-write-queue-order.c', 'test/test-threadpool.c', 'test/test-threadpool-cancel.c', 'test/test-mutexes.c', @@ -394,6 +397,7 @@ 'test/test-udp-open.c', 'test/test-udp-options.c', 'test/test-udp-send-and-recv.c', + 'test/test-udp-send-immediate.c', 'test/test-udp-multicast-join.c', 'test/test-udp-multicast-join6.c', 'test/test-dlerror.c', @@ -402,6 +406,7 @@ 'test/test-ip6-addr.c', 'test/test-udp-multicast-interface.c', 'test/test-udp-multicast-interface6.c', + 'test/test-udp-try-send.c', ], 'conditions': [ [ 'OS=="win"', { @@ -409,7 +414,7 @@ 'test/runner-win.c', 'test/runner-win.h' ], - 'libraries': [ 'ws2_32.lib' ] + 'libraries': [ '-lws2_32' ] }, { # POSIX 'defines': [ '_GNU_SOURCE' ], 'sources': [ @@ -473,7 +478,7 @@ 'test/runner-win.c', 'test/runner-win.h', ], - 'libraries': [ 'ws2_32.lib' ] + 'libraries': [ '-lws2_32' ] }, { # POSIX 'defines': [ '_GNU_SOURCE' ], 'sources': [ -- cgit v1.2.1