diff --git a/src/libuv/.gitignore b/src/libuv/.gitignore index 3f4c5193..afa05394 100644 --- a/src/libuv/.gitignore +++ b/src/libuv/.gitignore @@ -3,6 +3,7 @@ *.l[oa] *.opensdf *.orig +*.pyc *.sdf *.suo core diff --git a/src/libuv/.mailmap b/src/libuv/.mailmap index 5dc4075e..7ece4373 100644 --- a/src/libuv/.mailmap +++ b/src/libuv/.mailmap @@ -1,14 +1,23 @@ Alan Gutierrez +Andrius Bentkus Bert Belder Bert Belder Brandon Philips Brian White Brian White +Fedor Indutny Frank Denis Isaac Z. Schlueter +Justin Venus +Keno Fischer +Keno Fischer +Maciej Małecki +Marc Schlaich +Rasmus Pedersen Robert Mustacchi Ryan Dahl Ryan Emery +Sam Roberts San-Tai Hsu Saúl Ibarra Corretgé Shigeki Ohtsu diff --git a/src/libuv/AUTHORS b/src/libuv/AUTHORS index 119773a5..b27d1771 100644 --- a/src/libuv/AUTHORS +++ b/src/libuv/AUTHORS @@ -85,3 +85,49 @@ Miroslav Bajtoš Elliot Saba Wynn Wilkes Andrei Sedoi +Chris Bank +Geert Jansen +Alex Gaynor +huxingyi +Alex Crichton +Luca Bruno +Trevor Norris +Oguz Bastemur +Alexis Campailla +Justin Venus +Ben Kelly +Kristian Evensen +Sean Silva +Linus Mårtensson +Navaneeth Kedaram Nambiathan +Brent Cook +Brian Kaisner +Reini Urban +Maks Naumov +Sean Farrell +Christoph Iserlohn +Steven Kabbes +Tenor Biel +Andrej Manduch +Joshua Neuheisel +Yorkie +Sam Roberts +River Tarnell +Nathan Sweet +Dylan Cali +Austin Foxley +Geoffry Song +Benjamin Saunders +Rasmus Pedersen +William Light +Oleg Efimov +Lars Gierth +StarWing +thierry-FreeBSD +Isaiah Norton +Raul Martins +David Capello +Paul Tan +Javier Hernández +Tonis Tiigi +Michael Hudson-Doyle diff --git a/src/libuv/ChangeLog b/src/libuv/ChangeLog index 6ffffc77..3f010a0c 100644 --- a/src/libuv/ChangeLog +++ b/src/libuv/ChangeLog @@ -1,4 +1,194 @@ -2013.07.26, Version 0.10.13 (Stable) +2014.10.21, Version 0.10.29 (Stable) + +Changes since version 0.10.28: + +* darwin: allocate enough space for select() hack (Fedor Indutny) + +* linux: try epoll_pwait if epoll_wait is missing (Michael Hudson-Doyle) + +* windows: map ERROR_INVALID_DRIVE to UV_ENOENT (Saúl Ibarra Corretgé) + + +2014.07.32, Version 0.10.28 (Stable), 9c14b616f5fb84bfd7d45707bab4bbb85894443e + +Changes since version 0.10.27: + +* windows: fix handling closed socket while poll handle is closing (Saúl Ibarra + Corretgé) + +* unix: return system error on EAI_SYSTEM (Saúl Ibarra Corretgé) + +* unix: fix bogus structure field name (Saúl Ibarra Corretgé) + +* darwin: invoke `mach_timebase_info` only once (Fedor Indutny) + + +2014.05.02, Version 0.10.27 (Stable), 6e24ce23b1e7576059f85a608eca13b766458a01 + +Changes since version 0.10.26: + +* windows: fix console signal handler refcount (Saúl Ibarra Corretgé) + +* win: always leave crit section in get_proc_title (Fedor Indutny) + + +2014.04.07, Version 0.10.26 (Stable), d864907611c25ec986c5e77d4d6d6dee88f26926 + +Changes since version 0.10.25: + +* process: don't close stdio fds during spawn (Tonis Tiigi) + +* build, windows: do not fail on Windows SDK Prompt (Marc Schlaich) + +* build, windows: fix x64 configuration issue (Marc Schlaich) + +* win: fix buffer leak on error in pipe.c (Fedor Indutny) + +* kqueue: invalidate fd in uv_fs_event_t (Fedor Indutny) + +* linux: always deregister closing fds from epoll (Geoffry Song) + +* error: add ENXIO for O_NONBLOCK FIFO open() (Fedor Indutny) + + +2014.02.19, Version 0.10.25 (Stable), d778dc588507588b12b9f9d2905078db542ed751 + +Changes since version 0.10.24: + +* stream: start thread after assignments (Oguz Bastemur) + +* unix: correct error when calling uv_shutdown twice (Saúl Ibarra Corretgé) + +* windows: freeze in uv_tcp_endgame (Alexis Campailla) + +* sunos: handle rearm errors (Fedor Indutny) + + +2014.01.30, Version 0.10.24 (Stable), aecd296b6bce9b40f06a61c5c94e43d45ac7308a + +Changes since version 0.10.23: + +* linux: move sscanf() out of the assert() (Trevor Norris) + +* linux: fix C99/C++ comment (Fedor Indutny) + + +2014.01.23, Version 0.10.23 (Stable), dbd218e699fec8be311d85e4788be9e28ae884f8 + +Changes since version 0.10.22: + +* linux: relax assumption on /proc/stat parsing (Luca Bruno) + +* openbsd: fix obvious bug in uv_cpu_info (Fedor Indutny) + +* process: close stdio after dup2'ing it (Fedor Indutny) + + +2014.01.08, Version 0.10.22 (Stable), f526c90eeff271d9323a9107b9a64a4671fd3103 + +Changes since version 0.10.21: + +* windows: avoid assertion failure when pipe server is closed (Bert Belder) + + +2013.12.19, Version 0.10.21 (Stable), 375ebce068555f0ca8151b562edb5f1b263022db + +Changes since version 0.10.20: + +* unix: fix a possible memory leak in uv_fs_readdir (Alex Crichton) + + +2013.12.13, Version 0.10.20 (Stable), 04141464dd0fba90ace9aa6f7003ce139b888a40 + +Changes since version 0.10.19: + +* linux: fix up SO_REUSEPORT back-port (Ben Noordhuis) + +* fs-event: fix invalid memory access (huxingyi) + + +2013.11.13, Version 0.10.19 (Stable), 33959f7524090b8d2c6c41e2400ca77e31755059 + +Changes since version 0.10.18: + +* darwin: avoid calling GetCurrentProcess (Fedor Indutny) + +* unix: update events from pevents between polls (Fedor Indutny) + +* fsevents: support japaneese characters in path (Chris Bank) + +* linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis) + +* build: fix windows smp build with gyp (Geert Jansen) + +* linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis) + +* unix: fix reopened fd bug (Fedor Indutny) + +* core: fix fake watcher list and count preservation (Fedor Indutny) + + +2013.10.19, Version 0.10.18 (Stable), 9ec52963b585e822e87bdc5de28d6143aff0d2e5 + +Changes since version 0.10.17: + +* unix: fix uv_spawn() NULL pointer deref on ENOMEM (Ben Noordhuis) + +* unix: don't close inherited fds on uv_spawn() fail (Ben Noordhuis) + +* unix: revert recent FSEvent changes (Ben Noordhuis) + +* unix: fix non-synchronized access in signal.c (Ben Noordhuis) + + +2013.09.25, Version 0.10.17 (Stable), 9670e0a93540c2f0d86c84a375f2303383c11e7e + +Changes since version 0.10.16: + +* build: remove GCC_WARN_ABOUT_MISSING_NEWLINE (Ben Noordhuis) + +* darwin: fix 10.6 build error in fsevents.c (Ben Noordhuis) + + +2013.09.06, Version 0.10.16 (Stable), 2bce230d81f4853a23662cbeb26fe98010b1084b + +Changes since version 0.10.15: + +* windows: make uv_shutdown() for write-only pipes work (Bert Belder) + +* windows: make uv_fs_open() report EINVAL when invalid arguments are passed + (Bert Belder) + +* windows: make uv_fs_open() report _open_osfhandle() failure correctly (Bert + Belder) + +* windows: make uv_fs_chmod() report errors correctly (Bert Belder) + +* windows: wrap multi-statement macros in do..while block (Bert Belder) + + +2013.08.24, Version 0.10.15 (Stable), 221078a8fdd9b853c6b557b3d9a5dd744b4fdd6b + +Changes since version 0.10.14: + +* fsevents: create FSEvents thread on demand (Ben Noordhuis) + +* fsevents: use a single thread for interacting with FSEvents, because it's not + thread-safe. (Fedor Indutny) + +* fsevents: share FSEventStream between multiple FS watchers, which removes a + limit on the maximum number of file watchers that can be created on OS X. + (Fedor Indutny) + + +2013.08.22, Version 0.10.14 (Stable), 15d64132151c18b26346afa892444b95e2addad0 + +Changes since version 0.10.13: + +* unix: retry waitpid() on EINTR (Ben Noordhuis) + + +2013.07.26, Version 0.10.13 (Stable), 381312e1fe6fecbabc943ccd56f0e7d114b3d064 Changes since version 0.10.12: diff --git a/src/libuv/README.md b/src/libuv/README.md index 54740ca9..1e1aa25c 100644 --- a/src/libuv/README.md +++ b/src/libuv/README.md @@ -91,12 +91,12 @@ Or: Unix users run - ./gyp_uv -f make + ./gyp_uv.py -f make make -C out Macintosh users run - ./gyp_uv -f xcode + ./gyp_uv.py -f xcode xcodebuild -project uv.xcodeproj -configuration Release -target All Note for UNIX users: compile your project with `-D_LARGEFILE_SOURCE` and diff --git a/src/libuv/build.mk b/src/libuv/build.mk index 6cba169f..a2fdac7d 100644 --- a/src/libuv/build.mk +++ b/src/libuv/build.mk @@ -74,6 +74,7 @@ TESTS= \ test/test-delayed-accept.o \ test/test-dlerror.o \ test/test-embed.o \ + test/test-emfile.o \ test/test-error.o \ test/test-fail-always.o \ test/test-fs.o \ @@ -97,9 +98,11 @@ TESTS= \ test/test-ping-pong.o \ test/test-pipe-bind-error.o \ test/test-pipe-connect-error.o \ + test/test-pipe-server-close.o \ test/test-platform-output.o \ test/test-poll.o \ test/test-poll-close.o \ + test/test-poll-closesocket.o \ test/test-process-title.o \ test/test-ref.o \ test/test-run-nowait.o \ @@ -107,6 +110,7 @@ TESTS= \ test/test-semaphore.o \ test/test-shutdown-close.o \ test/test-shutdown-eof.o \ + test/test-shutdown-twice.o \ test/test-signal.o \ test/test-signal-multiple-loops.o \ test/test-spawn.o \ @@ -114,6 +118,7 @@ TESTS= \ test/test-tcp-bind6-error.o \ test/test-tcp-bind-error.o \ test/test-tcp-close.o \ + test/test-tcp-close-accept.o \ test/test-tcp-close-while-connecting.o \ test/test-tcp-connect6-error.o \ test/test-tcp-connect-error-after-write.o \ @@ -142,6 +147,7 @@ TESTS= \ test/test-udp-send-and-recv.o \ test/test-util.o \ test/test-walk-handles.o \ + test/test-watcher-cross-stop.o \ .PHONY: all bench clean clean-platform distclean test diff --git a/src/libuv/checksparse.sh b/src/libuv/checksparse.sh index 7412c21c..01fac07a 100755 --- a/src/libuv/checksparse.sh +++ b/src/libuv/checksparse.sh @@ -116,6 +116,7 @@ test/test-pass-always.c test/test-ping-pong.c test/test-pipe-bind-error.c test/test-pipe-connect-error.c +test/test-pipe-server-close.c test/test-platform-output.c test/test-poll-close.c test/test-poll.c @@ -133,6 +134,7 @@ test/test-stdio-over-pipes.c test/test-tcp-bind-error.c test/test-tcp-bind6-error.c test/test-tcp-close-while-connecting.c +test/test-tcp-close-accept.c test/test-tcp-close.c test/test-tcp-connect-error-after-write.c test/test-tcp-connect-error.c @@ -161,6 +163,7 @@ test/test-udp-options.c test/test-udp-send-and-recv.c test/test-util.c test/test-walk-handles.c +test/test-watcher-cross-stop.c " case `uname -s` in diff --git a/src/libuv/common.gypi b/src/libuv/common.gypi index 67291fdc..4b240d98 100644 --- a/src/libuv/common.gypi +++ b/src/libuv/common.gypi @@ -171,7 +171,6 @@ 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics - 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof 'PREBINDING': 'NO', # No -Wl,-prebind 'USE_HEADERMAP': 'NO', 'OTHER_CFLAGS': [ diff --git a/src/libuv/config-unix.mk b/src/libuv/config-unix.mk index d230bb25..37c4a72f 100644 --- a/src/libuv/config-unix.mk +++ b/src/libuv/config-unix.mk @@ -85,6 +85,7 @@ ifeq (__clang__,$(shell sh -c "$(CC) -dM -E - nwatchers) return; - nwatchers = next_power_of_two(len); - watchers = realloc(loop->watchers, nwatchers * sizeof(loop->watchers[0])); + /* Preserve fake watcher list and count at the end of the watchers */ + if (loop->watchers != NULL) { + fake_watcher_list = loop->watchers[loop->nwatchers]; + fake_watcher_count = loop->watchers[loop->nwatchers + 1]; + } else { + fake_watcher_list = NULL; + fake_watcher_count = NULL; + } + + nwatchers = next_power_of_two(len + 2) - 2; + watchers = realloc(loop->watchers, + (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) abort(); - for (i = loop->nwatchers; i < nwatchers; i++) watchers[i] = NULL; + watchers[nwatchers] = fake_watcher_list; + watchers[nwatchers + 1] = fake_watcher_count; loop->watchers = watchers; loop->nwatchers = nwatchers; @@ -700,6 +713,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { void uv__io_close(uv_loop_t* loop, uv__io_t* w) { uv__io_stop(loop, w, UV__POLLIN | UV__POLLOUT); ngx_queue_remove(&w->pending_queue); + + /* Remove stale events for this file descriptor */ + uv__platform_invalidate_fd(loop, w->fd); } diff --git a/src/libuv/src/unix/darwin-proctitle.c b/src/libuv/src/unix/darwin-proctitle.c index c3171a61..037acffb 100644 --- a/src/libuv/src/unix/darwin-proctitle.c +++ b/src/libuv/src/unix/darwin-proctitle.c @@ -36,14 +36,22 @@ int uv__set_process_title(const char* title) { CFStringRef, CFStringRef, CFDictionaryRef*); + typedef CFDictionaryRef (*LSApplicationCheckInType)(int, CFDictionaryRef); + typedef OSStatus (*SetApplicationIsDaemonType)(int); + typedef void (*LSSetApplicationLaunchServicesServerConnectionStatusType)( + uint64_t, void*); CFBundleRef launch_services_bundle; LSGetCurrentApplicationASNType ls_get_current_application_asn; LSSetApplicationInformationItemType ls_set_application_information_item; CFStringRef* display_name_key; - ProcessSerialNumber psn; CFTypeRef asn; CFStringRef display_name; OSStatus err; + CFBundleRef hi_services_bundle; + LSApplicationCheckInType ls_application_check_in; + SetApplicationIsDaemonType set_application_is_daemon; + LSSetApplicationLaunchServicesServerConnectionStatusType + ls_set_application_launch_services_server_connection_status; launch_services_bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); @@ -71,8 +79,36 @@ int uv__set_process_title(const char* title) { if (display_name_key == NULL || *display_name_key == NULL) return -1; - /* Force the process manager to initialize. */ - GetCurrentProcess(&psn); + /* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */ + hi_services_bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIServices")); + if (hi_services_bundle == NULL) + return -1; + + set_application_is_daemon = CFBundleGetFunctionPointerForName( + hi_services_bundle, + CFSTR("SetApplicationIsDaemon")); + ls_application_check_in = CFBundleGetFunctionPointerForName( + launch_services_bundle, + CFSTR("_LSApplicationCheckIn")); + ls_set_application_launch_services_server_connection_status = + CFBundleGetFunctionPointerForName( + launch_services_bundle, + CFSTR("_LSSetApplicationLaunchServicesServerConnectionStatus")); + if (set_application_is_daemon == NULL || + ls_application_check_in == NULL || + ls_set_application_launch_services_server_connection_status == NULL) { + return -1; + } + + if (set_application_is_daemon(1) != noErr) + return -1; + + ls_set_application_launch_services_server_connection_status(0, NULL); + + /* Check into process manager?! */ + ls_application_check_in(-2, + CFBundleGetInfoDictionary(CFBundleGetMainBundle())); display_name = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); asn = ls_get_current_application_asn(); diff --git a/src/libuv/src/unix/darwin.c b/src/libuv/src/unix/darwin.c index 77e662f4..f37d58df 100644 --- a/src/libuv/src/unix/darwin.c +++ b/src/libuv/src/unix/darwin.c @@ -179,12 +179,14 @@ void uv__cf_loop_signal(uv_loop_t* loop, cf_loop_signal_cb cb, void* arg) { uint64_t uv__hrtime(void) { - mach_timebase_info_data_t info; + static mach_timebase_info_data_t info; - if (mach_timebase_info(&info) != KERN_SUCCESS) - abort(); + if ((ACCESS_ONCE(uint32_t, info.numer) == 0 || + ACCESS_ONCE(uint32_t, info.denom) == 0) && + mach_timebase_info(&info) != KERN_SUCCESS) + abort(); - return mach_absolute_time() * info.numer / info.denom; + return mach_absolute_time() * info.numer / info.denom; } diff --git a/src/libuv/src/unix/error.c b/src/libuv/src/unix/error.c index 05ab4820..f54ffdbc 100644 --- a/src/libuv/src/unix/error.c +++ b/src/libuv/src/unix/error.c @@ -104,6 +104,12 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case EROFS: return UV_EROFS; case ENOMEM: return UV_ENOMEM; case EDQUOT: return UV_ENOSPC; + case EFBIG: return UV_EFBIG; + case ENOPROTOOPT: return UV_ENOPROTOOPT; + case ETXTBSY: return UV_ETXTBSY; + case ERANGE: return UV_ERANGE; + case ENXIO: return UV_ENXIO; + case EMLINK: return UV_EMLINK; default: return UV_UNKNOWN; } UNREACHABLE(); diff --git a/src/libuv/src/unix/fs.c b/src/libuv/src/unix/fs.c index dde1d3a8..4cfa6b80 100644 --- a/src/libuv/src/unix/fs.c +++ b/src/libuv/src/unix/fs.c @@ -202,9 +202,12 @@ static ssize_t uv__fs_readdir(uv_fs_t* req) { int i; int n; + dents = NULL; n = scandir(req->path, &dents, uv__fs_readdir_filter, alphasort); - if (n == -1 || n == 0) + if (n == 0) + goto out; /* osx still needs to deallocate some memory */ + else if (n == -1) return n; len = 0; @@ -232,7 +235,7 @@ static ssize_t uv__fs_readdir(uv_fs_t* req) { out: saved_errno = errno; - { + if (dents != NULL) { for (i = 0; i < n; i++) free(dents[i]); free(dents); @@ -302,7 +305,7 @@ static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) { * * 1. Read errors are reported only if nsent==0, otherwise we return nsent. * The user needs to know that some data has already been sent, to stop - * him from sending it twice. + * them from sending it twice. * * 2. Write errors are always reported. Write errors are bad because they * mean data loss: we've read data but now we can't write it out. diff --git a/src/libuv/src/unix/fsevents.c b/src/libuv/src/unix/fsevents.c index b6d27467..d8603242 100644 --- a/src/libuv/src/unix/fsevents.c +++ b/src/libuv/src/unix/fsevents.c @@ -223,9 +223,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) { handle->realpath_len = strlen(handle->realpath); /* Initialize paths array */ - path = CFStringCreateWithCString(NULL, - handle->filename, - CFStringGetSystemEncoding()); + path = CFStringCreateWithFileSystemRepresentation(NULL, handle->filename); paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL); latency = 0.15; diff --git a/src/libuv/src/unix/getaddrinfo.c b/src/libuv/src/unix/getaddrinfo.c index 283d295f..287c9940 100644 --- a/src/libuv/src/unix/getaddrinfo.c +++ b/src/libuv/src/unix/getaddrinfo.c @@ -34,6 +34,8 @@ static void uv__getaddrinfo_work(struct uv__work* w) { req->service, req->hints, &req->res); + if (req->retcode == EAI_SYSTEM) + req->retcode = -errno; } @@ -67,7 +69,10 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) { req->service = NULL; req->hostname = NULL; - if (req->retcode == 0) { + if (req->retcode < 0) { + /* EAI_SYSTEM error */ + uv__set_sys_error(req->loop, -req->retcode); + } else if (req->retcode == 0) { /* OK */ #if defined(EAI_NODATA) /* FreeBSD deprecated EAI_NODATA */ } else if (req->retcode == EAI_NONAME || req->retcode == EAI_NODATA) { diff --git a/src/libuv/src/unix/internal.h b/src/libuv/src/unix/internal.h index 61cb1ec1..35aa894a 100644 --- a/src/libuv/src/unix/internal.h +++ b/src/libuv/src/unix/internal.h @@ -183,6 +183,7 @@ uint64_t uv__hrtime(void); int uv__kqueue_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop, int default_loop); void uv__platform_loop_delete(uv_loop_t* loop); +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); /* various */ void uv__async_close(uv_async_t* handle); diff --git a/src/libuv/src/unix/kqueue.c b/src/libuv/src/unix/kqueue.c index 378903a6..124f4fd5 100644 --- a/src/libuv/src/unix/kqueue.c +++ b/src/libuv/src/unix/kqueue.c @@ -162,9 +162,15 @@ void uv__io_poll(uv_loop_t* loop, int 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++) { ev = events + i; fd = ev->ident; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; w = loop->watchers[fd]; if (w == NULL) { @@ -191,7 +197,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { revents = 0; if (ev->filter == EVFILT_READ) { - if (w->events & UV__POLLIN) { + if (w->pevents & UV__POLLIN) { revents |= UV__POLLIN; w->rcount = ev->data; } else { @@ -205,7 +211,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } if (ev->filter == EVFILT_WRITE) { - if (w->events & UV__POLLOUT) { + if (w->pevents & UV__POLLOUT) { revents |= UV__POLLOUT; w->wcount = ev->data; } else { @@ -227,6 +233,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->cb(loop, w, revents); nevents++; } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { @@ -255,6 +263,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct kevent* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct kevent*) 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].ident == fd) + events[i].ident = -1; +} + + static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { uv_fs_event_t* handle; struct kevent ev; @@ -332,10 +359,10 @@ int uv_fs_event_init(uv_loop_t* loop, void uv__fs_event_close(uv_fs_event_t* handle) { #if defined(__APPLE__) if (uv__fsevents_close(handle)) - uv__io_stop(handle->loop, &handle->event_watcher, UV__POLLIN); -#else - uv__io_stop(handle->loop, &handle->event_watcher, UV__POLLIN); #endif /* defined(__APPLE__) */ + { + uv__io_close(handle->loop, &handle->event_watcher); + } uv__handle_stop(handle); diff --git a/src/libuv/src/unix/linux-core.c b/src/libuv/src/unix/linux-core.c index e4c34a18..4846bd3f 100644 --- a/src/libuv/src/unix/linux-core.c +++ b/src/libuv/src/unix/linux-core.c @@ -97,6 +97,33 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct uv__epoll_event* events; + struct uv__epoll_event dummy; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events != NULL) + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].data == fd) + events[i].data = -1; + + /* Remove the file descriptor from the epoll. + * This avoids a problem where the same file description remains open + * in another process, causing repeated junk epoll events. + * + * We pass in a dummy epoll_event, to work around a bug in old kernels. + */ + if (loop->backend_fd >= 0) + uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &dummy); +} + + void uv__io_poll(uv_loop_t* loop, int timeout) { struct uv__epoll_event events[1024]; struct uv__epoll_event* pe; @@ -111,6 +138,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; + static int no_epoll_wait; if (loop->nfds == 0) { assert(ngx_queue_empty(&loop->watcher_queue)); @@ -157,10 +185,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { - nfds = uv__epoll_wait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout); + if (!no_epoll_wait) { + nfds = uv__epoll_wait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + if (nfds == -1 && errno == ENOSYS) { + no_epoll_wait = 1; + continue; + } + } else { + nfds = uv__epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + NULL); + } /* 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 @@ -189,10 +229,17 @@ void uv__io_poll(uv_loop_t* loop, int 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; fd = pe->data; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); @@ -208,9 +255,38 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; } - w->cb(loop, w, pe->events); - nevents++; + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | UV__POLLERR | UV__POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == UV__EPOLLERR || pe->events == UV__EPOLLHUP) + pe->events |= w->pevents & (UV__EPOLLIN | UV__EPOLLOUT); + + if (pe->events != 0) { + w->cb(loop, w, pe->events); + nevents++; + } } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { @@ -546,9 +622,11 @@ static int read_times(unsigned int numcpus, uv_cpu_info_t* ci) { /* skip "cpu " marker */ { - unsigned int n = num; + unsigned int n; + int r = sscanf(buf, "cpu%u ", &n); + assert(r == 1); + (void) r; /* silence build warning */ for (len = sizeof("cpu0"); n /= 10; len++); - assert(sscanf(buf, "cpu%u ", &n) == 1 && n == num); } /* Line contains user, nice, system, idle, iowait, irq, softirq, steal, @@ -575,6 +653,7 @@ static int read_times(unsigned int numcpus, uv_cpu_info_t* ci) { ci[num++].cpu_times = ts; } fclose(fp); + assert(num == numcpus); return 0; } diff --git a/src/libuv/src/unix/openbsd.c b/src/libuv/src/unix/openbsd.c index 4dfcd76b..ec83a818 100644 --- a/src/libuv/src/unix/openbsd.c +++ b/src/libuv/src/unix/openbsd.c @@ -223,7 +223,7 @@ uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { uint64_t info[CPUSTATES]; char model[512]; int numcpus = 1; - static int which[] = {CTL_HW,HW_MODEL,0}; + int which[] = {CTL_HW,HW_MODEL,0}; size_t size; int i; uv_cpu_info_t* cpu_info; diff --git a/src/libuv/src/unix/process.c b/src/libuv/src/unix/process.c index 267ecb64..19686a29 100644 --- a/src/libuv/src/unix/process.c +++ b/src/libuv/src/unix/process.c @@ -73,7 +73,9 @@ static void uv__chld(uv_signal_t* handle, int signum) { assert(signum == SIGCHLD); for (;;) { - pid = waitpid(-1, &status, WNOHANG); + do + pid = waitpid(-1, &status, WNOHANG); + while (pid == -1 && errno == EINTR); if (pid == 0) return; @@ -184,7 +186,7 @@ int uv__make_pipe(int fds[2], int flags) { /* * Used for initializing stdio streams like options.stdin_stream. Returns - * zero on success. + * zero on success. See also the cleanup section in uv_spawn(). */ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { int mask; @@ -288,49 +290,54 @@ static void uv__process_child_init(uv_process_options_t options, close_fd = pipes[fd][0]; use_fd = pipes[fd][1]; - if (use_fd >= 0) - close(close_fd); - else if (fd >= 3) - continue; - else { - /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is - * set - */ - use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); - - if (use_fd == -1) { - uv__write_int(error_fd, errno); - perror("failed to open stdio"); - _exit(127); + if (use_fd < 0) { + if (fd >= 3) + continue; + else { + /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is + * set + */ + use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); + close_fd = use_fd; + + if (use_fd == -1) { + uv__write_int(error_fd, errno); + _exit(127); + } } } if (fd == use_fd) uv__cloexec(use_fd, 0); - else { + else dup2(use_fd, fd); - close(use_fd); - } if (fd <= 2) uv__nonblock(fd, 0); + + if (close_fd >= stdio_count) + close(close_fd); + } + + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + + if (use_fd >= 0 && fd != use_fd) + close(use_fd); } if (options.cwd && chdir(options.cwd)) { uv__write_int(error_fd, errno); - perror("chdir()"); _exit(127); } if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) { uv__write_int(error_fd, errno); - perror("setgid()"); _exit(127); } if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) { uv__write_int(error_fd, errno); - perror("setuid()"); _exit(127); } @@ -340,7 +347,6 @@ static void uv__process_child_init(uv_process_options_t options, execvp(options.file, options.args); uv__write_int(error_fd, errno); - perror("execvp()"); _exit(127); } @@ -461,11 +467,18 @@ int uv_spawn(uv_loop_t* loop, error: uv__set_sys_error(process->loop, errno); - for (i = 0; i < stdio_count; i++) { - close(pipes[i][0]); - close(pipes[i][1]); + if (pipes != NULL) { + for (i = 0; i < stdio_count; i++) { + if (i < options.stdio_count) + if (options.stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM)) + continue; + if (pipes[i][0] != -1) + close(pipes[i][0]); + if (pipes[i][1] != -1) + close(pipes[i][1]); + } + free(pipes); } - free(pipes); return -1; } diff --git a/src/libuv/src/unix/signal.c b/src/libuv/src/unix/signal.c index 22c7783b..aa84ff2f 100644 --- a/src/libuv/src/unix/signal.c +++ b/src/libuv/src/unix/signal.c @@ -141,7 +141,10 @@ static void uv__signal_handler(int signum) { saved_errno = errno; memset(&msg, 0, sizeof msg); - uv__signal_lock(); + if (uv__signal_lock()) { + errno = saved_errno; + return; + } for (handle = uv__signal_first_handle(signum); handle != NULL && handle->signum == signum; diff --git a/src/libuv/src/unix/stream.c b/src/libuv/src/unix/stream.c index c9df9797..81933e64 100644 --- a/src/libuv/src/unix/stream.c +++ b/src/libuv/src/unix/stream.c @@ -53,6 +53,10 @@ struct uv__stream_select_s { int fake_fd; int int_fd; int fd; + fd_set* sread; + size_t sread_sz; + fd_set* swrite; + size_t swrite_sz; }; #endif /* defined(__APPLE__) */ @@ -131,8 +135,6 @@ static void uv__stream_osx_select(void* arg) { uv_stream_t* stream; uv__stream_select_t* s; char buf[1024]; - fd_set sread; - fd_set swrite; int events; int fd; int r; @@ -153,17 +155,17 @@ static void uv__stream_osx_select(void* arg) { break; /* Watch fd using select(2) */ - FD_ZERO(&sread); - FD_ZERO(&swrite); + memset(s->sread, 0, s->sread_sz); + memset(s->swrite, 0, s->swrite_sz); if (uv_is_readable(stream)) - FD_SET(fd, &sread); + FD_SET(fd, s->sread); if (uv_is_writable(stream)) - FD_SET(fd, &swrite); - FD_SET(s->int_fd, &sread); + FD_SET(fd, s->swrite); + FD_SET(s->int_fd, s->sread); /* Wait indefinitely for fd events */ - r = select(max_fd + 1, &sread, &swrite, NULL, NULL); + r = select(max_fd + 1, s->sread, s->swrite, NULL, NULL); if (r == -1) { if (errno == EINTR) continue; @@ -177,7 +179,7 @@ static void uv__stream_osx_select(void* arg) { continue; /* Empty socketpair's buffer in case of interruption */ - if (FD_ISSET(s->int_fd, &sread)) + if (FD_ISSET(s->int_fd, s->sread)) while (1) { r = read(s->int_fd, buf, sizeof(buf)); @@ -198,12 +200,12 @@ static void uv__stream_osx_select(void* arg) { /* Handle events */ events = 0; - if (FD_ISSET(fd, &sread)) + if (FD_ISSET(fd, s->sread)) events |= UV__POLLIN; - if (FD_ISSET(fd, &swrite)) + if (FD_ISSET(fd, s->swrite)) events |= UV__POLLOUT; - assert(events != 0 || FD_ISSET(s->int_fd, &sread)); + assert(events != 0 || FD_ISSET(s->int_fd, s->sread)); if (events != 0) { ACCESS_ONCE(int, s->events) = events; @@ -282,6 +284,11 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { int fds[2]; int ret; int kq; + int old_fd; + int max_fd; + size_t sread_sz; + size_t swrite_sz; + int err; kq = kqueue(); if (kq == -1) { @@ -305,55 +312,92 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { return 0; /* At this point we definitely know that this fd won't work with kqueue */ - s = malloc(sizeof(*s)); - if (s == NULL) - return uv__set_artificial_error(stream->loop, UV_ENOMEM); + + /* + * Create fds for io watcher and to interrupt the select() loop. + * NOTE: do it ahead of malloc below to allocate enough space for fd_sets + */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) + return uv__set_sys_error(stream->loop, errno); + + max_fd = *fd; + if (fds[1] > max_fd) + max_fd = fds[1]; + + sread_sz = (max_fd + NBBY) / NBBY; + swrite_sz = sread_sz; + + s = malloc(sizeof(*s) + sread_sz + swrite_sz); + if (s == NULL) { + err = uv__set_artificial_error(stream->loop, UV_ENOMEM); + goto failed_malloc; + } s->events = 0; s->fd = *fd; + s->sread = (fd_set*) ((char*) s + sizeof(*s)); + s->sread_sz = sread_sz; + s->swrite = (fd_set*) ((char*) s->sread + sread_sz); + s->swrite_sz = swrite_sz; - if (uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb)) { - SAVE_ERRNO(free(s)); - return uv__set_sys_error(stream->loop, errno); - } + err = uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb); + if (err) + goto failed_async_init; s->async.flags |= UV__HANDLE_INTERNAL; uv__handle_unref(&s->async); - if (uv_sem_init(&s->close_sem, 0)) - goto fatal1; - - if (uv_sem_init(&s->async_sem, 0)) - goto fatal2; + err = uv_sem_init(&s->close_sem, 0); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_close_sem_init; + } - /* Create fds for io watcher and to interrupt the select() loop. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - goto fatal3; + err = uv_sem_init(&s->async_sem, 0); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_async_sem_init; + } s->fake_fd = fds[0]; s->int_fd = fds[1]; - if (uv_thread_create(&s->thread, uv__stream_osx_select, stream)) - goto fatal4; - + old_fd = *fd; s->stream = stream; stream->select = s; *fd = s->fake_fd; + err = uv_thread_create(&s->thread, uv__stream_osx_select, stream); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_thread_create; + } + return 0; -fatal4: - close(s->fake_fd); - close(s->int_fd); - s->fake_fd = -1; - s->int_fd = -1; -fatal3: +failed_thread_create: + s->stream = NULL; + stream->select = NULL; + *fd = old_fd; uv_sem_destroy(&s->async_sem); -fatal2: + +failed_async_sem_init: uv_sem_destroy(&s->close_sem); -fatal1: + +failed_close_sem_init: + close(fds[0]); + close(fds[1]); uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); - return uv__set_sys_error(stream->loop, errno); + return err; + +failed_async_init: + free(s); + +failed_malloc: + close(fds[0]); + close(fds[1]); + + return err; } #endif /* defined(__APPLE__) */ @@ -679,8 +723,8 @@ static void uv__write_req_finish(uv_write_t* req) { /* Only free when there was no error. On error, we touch up write_queue_size * right before making the callback. The reason we don't do that right away * is that a write_queue_size > 0 is our only way to signal to the user that - * he should stop writing - which he should if we got an error. Something to - * revisit in future revisions of the libuv API. + * they should stop writing - which they should if we got an error. Something + * to revisit in future revisions of the libuv API. */ if (req->error == 0) { if (req->bufs != req->bufsml) @@ -1075,6 +1119,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { if (!(stream->flags & UV_STREAM_WRITABLE) || stream->flags & UV_STREAM_SHUT || + stream->flags & UV_STREAM_SHUTTING || stream->flags & UV_CLOSED || stream->flags & UV_CLOSING) { uv__set_artificial_error(stream->loop, UV_ENOTCONN); diff --git a/src/libuv/src/unix/sunos.c b/src/libuv/src/unix/sunos.c index 69769f65..9030bfa6 100644 --- a/src/libuv/src/unix/sunos.c +++ b/src/libuv/src/unix/sunos.c @@ -28,9 +28,6 @@ #include #include -#define SUNOS_NO_IFADDRS - - #ifndef SUNOS_NO_IFADDRS # include #endif @@ -90,6 +87,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct port_event* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + + events = (struct port_event*) 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].portev_object == fd) + events[i].portev_object = -1; +} + + void uv__io_poll(uv_loop_t* loop, int timeout) { struct port_event events[1024]; struct port_event* pe; @@ -176,10 +192,17 @@ void uv__io_poll(uv_loop_t* loop, int 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; fd = pe->portev_object; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); @@ -196,6 +219,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (w->pevents != 0 && ngx_queue_empty(&w->watcher_queue)) ngx_queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { @@ -278,9 +303,9 @@ void uv_loadavg(double avg[3]) { #if defined(PORT_SOURCE_FILE) -static void uv__fs_event_rearm(uv_fs_event_t *handle) { +static int uv__fs_event_rearm(uv_fs_event_t* handle) { if (handle->fd == -1) - return; + return 0; if (port_associate(handle->loop->fs_fd, PORT_SOURCE_FILE, @@ -288,8 +313,10 @@ static void uv__fs_event_rearm(uv_fs_event_t *handle) { FILE_ATTRIB | FILE_MODIFIED, handle) == -1) { uv__set_sys_error(handle->loop, errno); + return -1; } handle->fd = PORT_LOADED; + return 0; } @@ -336,11 +363,12 @@ static void uv__fs_event_read(uv_loop_t* loop, assert(events != 0); handle->fd = PORT_FIRED; handle->cb(handle, NULL, events, 0); + + if (handle->fd != PORT_DELETED) + if (uv__fs_event_rearm(handle) != 0) + handle->cb(handle, NULL, 0, -1); } while (handle->fd != PORT_DELETED); - - if (handle != NULL && handle->fd != PORT_DELETED) - uv__fs_event_rearm(handle); } @@ -362,14 +390,16 @@ int uv_fs_event_init(uv_loop_t* loop, } uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); - uv__handle_start(handle); /* FIXME shouldn't start automatically */ handle->filename = strdup(filename); handle->fd = PORT_UNUSED; handle->cb = cb; memset(&handle->fo, 0, sizeof handle->fo); handle->fo.fo_name = handle->filename; - uv__fs_event_rearm(handle); + if (uv__fs_event_rearm(handle) != 0) + return -1; + + uv__handle_start(handle); /* FIXME shouldn't start automatically */ if (first_run) { uv__io_init(&loop->fs_event_watcher, uv__fs_event_read, portfd); diff --git a/src/libuv/src/unix/udp.c b/src/libuv/src/unix/udp.c index 3fb8af93..e83e6133 100644 --- a/src/libuv/src/unix/udp.c +++ b/src/libuv/src/unix/udp.c @@ -286,6 +286,31 @@ static void uv__udp_sendmsg(uv_loop_t* loop, } +/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional + * refinements for programs that use multicast. + * + * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that + * are different from the BSDs: it _shares_ the port rather than steal it + * from the current listener. While useful, it's not something we can emulate + * on other platforms so we don't enable it. + */ +static int uv__set_reuse(int fd) { + int yes; + +#if defined(SO_REUSEPORT) && !defined(__linux__) + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) + return -errno; +#else + yes = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return -errno; +#endif + + return 0; +} + + static int uv__bind(uv_udp_t* handle, int domain, struct sockaddr* addr, @@ -293,6 +318,7 @@ static int uv__bind(uv_udp_t* handle, unsigned flags) { int saved_errno; int status; + int err; int yes; int fd; @@ -321,27 +347,11 @@ static int uv__bind(uv_udp_t* handle, } fd = handle->io_watcher.fd; - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); - goto out; - } - - /* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT - * state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets - * multiple processes bind to the same address. Yes, it's something of a - * misnomer but then again, SO_REUSEADDR was already taken. - * - * None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on - * Linux and hence it does not have SO_REUSEPORT at all. - */ -#ifdef SO_REUSEPORT - yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); + err = uv__set_reuse(fd); + if (err) { + uv__set_sys_error(handle->loop, -err); goto out; } -#endif if (flags & UV_UDP_IPV6ONLY) { #ifdef IPV6_V6ONLY @@ -481,7 +491,7 @@ int uv__udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags) { int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int saved_errno; int status; - int yes; + int err; saved_errno = errno; status = -1; @@ -492,27 +502,11 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { goto out; } - yes = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); - goto out; - } - - /* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT - * state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets - * multiple processes bind to the same address. Yes, it's something of a - * misnomer but then again, SO_REUSEADDR was already taken. - * - * None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on - * Linux and hence it does not have SO_REUSEPORT at all. - */ -#ifdef SO_REUSEPORT - yes = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) { - uv__set_sys_error(handle->loop, errno); + err = uv__set_reuse(sock); + if (err) { + uv__set_sys_error(handle->loop, -err); goto out; } -#endif handle->io_watcher.fd = sock; status = 0; diff --git a/src/libuv/src/version.c b/src/libuv/src/version.c index 1010b2af..ca964008 100644 --- a/src/libuv/src/version.c +++ b/src/libuv/src/version.c @@ -34,7 +34,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 13 +#define UV_VERSION_PATCH 29 #define UV_VERSION_IS_RELEASE 1 diff --git a/src/libuv/src/win/error.c b/src/libuv/src/win/error.c index 2a72ff2f..820c2603 100644 --- a/src/libuv/src/win/error.c +++ b/src/libuv/src/win/error.c @@ -128,6 +128,7 @@ uv_err_code 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/src/libuv/src/win/fs-event.c b/src/libuv/src/win/fs-event.c index 1954cb09..e1f79abd 100644 --- a/src/libuv/src/win/fs-event.c +++ b/src/libuv/src/win/fs-event.c @@ -339,7 +339,8 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); } - _snwprintf(filenamew, size, L"%s\\%s", handle->dirw, + _snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw, + file_info->FileNameLength / sizeof(WCHAR), file_info->FileName); filenamew[size - 1] = L'\0'; diff --git a/src/libuv/src/win/fs.c b/src/libuv/src/win/fs.c index e78bc1b8..7f3704e8 100644 --- a/src/libuv/src/win/fs.c +++ b/src/libuv/src/win/fs.c @@ -41,33 +41,41 @@ #define QUEUE_FS_TP_JOB(loop, req) \ - if (!QueueUserWorkItem(&uv_fs_thread_proc, \ - req, \ - WT_EXECUTEDEFAULT)) { \ - uv__set_sys_error((loop), GetLastError()); \ - return -1; \ - } \ - uv__req_register(loop, req); + do { \ + if (!QueueUserWorkItem(&uv_fs_thread_proc, \ + req, \ + WT_EXECUTEDEFAULT)) { \ + uv__set_sys_error((loop), GetLastError()); \ + return -1; \ + } \ + uv__req_register(loop, req); \ + } while (0) #define SET_UV_LAST_ERROR_FROM_REQ(req) \ - uv__set_error(req->loop, req->errorno, req->sys_errno_); + uv__set_error(req->loop, req->errorno, req->sys_errno_) #define SET_REQ_RESULT(req, result_value) \ - req->result = (result_value); \ - if (req->result == -1) { \ - req->sys_errno_ = _doserrno; \ - req->errorno = uv_translate_sys_error(req->sys_errno_); \ - } + do { \ + req->result = (result_value); \ + if (req->result == -1) { \ + req->sys_errno_ = _doserrno; \ + req->errorno = uv_translate_sys_error(req->sys_errno_); \ + } \ + } while (0) #define SET_REQ_WIN32_ERROR(req, sys_errno) \ - req->result = -1; \ - req->sys_errno_ = (sys_errno); \ - req->errorno = uv_translate_sys_error(req->sys_errno_); + do { \ + req->result = -1; \ + req->sys_errno_ = (sys_errno); \ + req->errorno = uv_translate_sys_error(req->sys_errno_); \ + } while (0) #define SET_REQ_UV_ERROR(req, uv_errno, sys_errno) \ - req->result = -1; \ - req->sys_errno_ = (sys_errno); \ - req->errorno = (uv_errno); + do { \ + req->result = -1; \ + req->sys_errno_ = (sys_errno); \ + req->errorno = (uv_errno); \ + } while (0) #define VERIFY_FD(fd, req) \ if (fd == -1) { \ @@ -78,7 +86,7 @@ } #define FILETIME_TO_TIME_T(filetime) \ - ((*((uint64_t*) &(filetime)) - 116444736000000000ULL) / 10000000ULL); + ((*((uint64_t*) &(filetime)) - 116444736000000000ULL) / 10000000ULL) #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ @@ -392,7 +400,7 @@ void fs__open(uv_fs_t* req) { DWORD disposition; DWORD attributes = 0; HANDLE file; - int result, current_umask; + int fd, current_umask; int flags = req->file_flags; /* Obtain the active umask. umask() never fails and returns the previous */ @@ -413,8 +421,7 @@ void fs__open(uv_fs_t* req) { access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; break; default: - result = -1; - goto end; + goto einval; } if (flags & _O_APPEND) { @@ -451,8 +458,7 @@ void fs__open(uv_fs_t* req) { disposition = CREATE_ALWAYS; break; default: - result = -1; - goto end; + goto einval; } attributes |= FILE_ATTRIBUTE_NORMAL; @@ -481,8 +487,7 @@ void fs__open(uv_fs_t* req) { attributes |= FILE_FLAG_RANDOM_ACCESS; break; default: - result = -1; - goto end; + goto einval; } /* Setting this flag makes it possible to open a directory. */ @@ -507,11 +512,30 @@ void fs__open(uv_fs_t* req) { } return; } - result = _open_osfhandle((intptr_t) file, flags); -end: - SET_REQ_RESULT(req, result); + + fd = _open_osfhandle((intptr_t) file, flags); + if (fd < 0) { + /* The only known failure mode for _open_osfhandle() is EMFILE, in which + * case GetLastError() will return zero. However we'll try to handle other + * errors as well, should they ever occur. + */ + if (errno == EMFILE) + SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); + else if (GetLastError() != ERROR_SUCCESS) + SET_REQ_WIN32_ERROR(req, GetLastError()); + else + SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); + return; + } + + SET_REQ_RESULT(req, fd); + return; + + einval: + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); } + void fs__close(uv_fs_t* req) { int fd = req->fd; int result; @@ -1062,7 +1086,6 @@ static void fs__chmod(uv_fs_t* req) { static void fs__fchmod(uv_fs_t* req) { int fd = req->fd; - int result; HANDLE handle; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; @@ -1070,7 +1093,7 @@ static void fs__fchmod(uv_fs_t* req) { VERIFY_FD(fd, req); - handle = (HANDLE)_get_osfhandle(fd); + handle = (HANDLE) _get_osfhandle(fd); nt_status = pNtQueryInformationFile(handle, &io_status, @@ -1078,9 +1101,9 @@ static void fs__fchmod(uv_fs_t* req) { sizeof file_info, FileBasicInformation); - if (nt_status != STATUS_SUCCESS) { - result = -1; - goto done; + if (!NT_SUCCESS(nt_status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); + return; } if (req->mode & _S_IWRITE) { @@ -1095,15 +1118,12 @@ static void fs__fchmod(uv_fs_t* req) { sizeof file_info, FileBasicInformation); - if (nt_status != STATUS_SUCCESS) { - result = -1; - goto done; + if (!NT_SUCCESS(nt_status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); + return; } - result = 0; - -done: - SET_REQ_RESULT(req, result); + SET_REQ_SUCCESS(req); } diff --git a/src/libuv/src/win/pipe.c b/src/libuv/src/win/pipe.c index 0fb70eae..ee0c0f88 100644 --- a/src/libuv/src/win/pipe.c +++ b/src/libuv/src/win/pipe.c @@ -1240,9 +1240,9 @@ static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, uv__set_artificial_error(loop, UV_EOF); if (handle->read2_cb) { - handle->read2_cb(handle, -1, uv_null_buf_, UV_UNKNOWN_HANDLE); + handle->read2_cb(handle, -1, buf, UV_UNKNOWN_HANDLE); } else { - handle->read_cb((uv_stream_t*) handle, -1, uv_null_buf_); + handle->read_cb((uv_stream_t*) handle, -1, buf); } } @@ -1404,7 +1404,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, break; } } else { - uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); + uv_pipe_read_error_or_eof(loop, handle, GetLastError(), buf); break; } } @@ -1480,6 +1480,13 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, assert(handle->type == UV_NAMED_PIPE); + if (handle->flags & UV__HANDLE_CLOSING) { + /* The req->pipeHandle should be freed already in uv_pipe_cleanup(). */ + assert(req->pipeHandle == INVALID_HANDLE_VALUE); + DECREASE_PENDING_REQ_COUNT(handle); + return; + } + if (REQ_SUCCESS(req)) { assert(req->pipeHandle != INVALID_HANDLE_VALUE); req->next_pending = handle->pending_accepts; @@ -1528,9 +1535,9 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, UNREGISTER_HANDLE_REQ(loop, handle, req); - /* Initialize and optionally start the eof timer. */ - /* This makes no sense if we've already seen EOF. */ if (handle->flags & UV_HANDLE_READABLE) { + /* Initialize and optionally start the eof timer. Only do this if the */ + /* pipe is readable and we haven't seen EOF come in ourselves. */ eof_timer_init(handle); /* If reading start the timer right now. */ @@ -1538,6 +1545,12 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, if (handle->flags & UV_HANDLE_READ_PENDING) { eof_timer_start(handle); } + + } else { + /* This pipe is not readable. We can just close it to let the other end */ + /* know that we're done writing. */ + CloseHandle(handle->handle); + handle->handle = INVALID_HANDLE_VALUE; } if (req->cb) { diff --git a/src/libuv/src/win/poll.c b/src/libuv/src/win/poll.c index f6972f96..76427b01 100644 --- a/src/libuv/src/win/poll.c +++ b/src/libuv/src/win/poll.c @@ -187,7 +187,8 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, if (afd_poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) { /* Stop polling. */ handle->events = 0; - uv__handle_stop(handle); + if (uv__is_active(handle)) + uv__handle_stop(handle); } if (events != 0) { diff --git a/src/libuv/src/win/process-stdio.c b/src/libuv/src/win/process-stdio.c index 89e2cd99..e9cf84da 100644 --- a/src/libuv/src/win/process-stdio.c +++ b/src/libuv/src/win/process-stdio.c @@ -104,12 +104,16 @@ static uv_err_t uv__create_stdio_pipe_pair(uv_loop_t* loop, uv_err_t err; if (flags & UV_READABLE_PIPE) { - server_access |= PIPE_ACCESS_OUTBOUND; + /* The server needs inbound access too, otherwise CreateNamedPipe() */ + /* won't give us the FILE_READ_ATTRIBUTES permission. We need that to */ + /* probe the state of the write buffer when we're trying to shutdown */ + /* the pipe. */ + server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; } if (flags & UV_WRITABLE_PIPE) { server_access |= PIPE_ACCESS_INBOUND; - client_access |= GENERIC_WRITE; + client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; } /* Create server pipe handle. */ @@ -163,8 +167,11 @@ static uv_err_t uv__create_stdio_pipe_pair(uv_loop_t* loop, } } - /* The server end is now readable and writable. */ - server_pipe->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; + /* The server end is now readable and/or writable. */ + if (flags & UV_READABLE_PIPE) + server_pipe->flags |= UV_HANDLE_WRITABLE; + if (flags & UV_WRITABLE_PIPE) + server_pipe->flags |= UV_HANDLE_READABLE; *child_pipe_ptr = child_pipe; return uv_ok_; diff --git a/src/libuv/src/win/signal.c b/src/libuv/src/win/signal.c index e630cd38..f2938355 100644 --- a/src/libuv/src/win/signal.c +++ b/src/libuv/src/win/signal.c @@ -129,8 +129,10 @@ static uv_err_t uv__signal_register_control_handler() { /* If the console control handler has already been hooked, just add a */ /* reference. */ - if (uv__signal_control_handler_refs > 0) + if (uv__signal_control_handler_refs > 0) { + uv__signal_control_handler_refs++; return uv_ok_; + } if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE)) return uv__new_sys_error(GetLastError()); diff --git a/src/libuv/src/win/tcp.c b/src/libuv/src/win/tcp.c index 59a36de0..a91c5784 100644 --- a/src/libuv/src/win/tcp.c +++ b/src/libuv/src/win/tcp.c @@ -577,6 +577,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { req->accept_socket = INVALID_SOCKET; req->data = handle; req->wait_handle = INVALID_HANDLE_VALUE; + req->event_handle = NULL; } } @@ -1040,9 +1041,11 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (req->wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(req->wait_handle); + req->wait_handle = INVALID_HANDLE_VALUE; } if (req->event_handle) { CloseHandle(req->event_handle); + req->event_handle = NULL; } } diff --git a/src/libuv/src/win/util.c b/src/libuv/src/win/util.c index 4dd07363..4ff23b6d 100644 --- a/src/libuv/src/win/util.c +++ b/src/libuv/src/win/util.c @@ -427,6 +427,7 @@ uv_err_t uv_get_process_title(char* buffer, size_t size) { * we must query it with getConsoleTitleW */ if (!process_title && uv__get_process_title() == -1) { + LeaveCriticalSection(&process_title_lock); return uv__new_sys_error(GetLastError()); } diff --git a/src/libuv/test/test-emfile.c b/src/libuv/test/test-emfile.c new file mode 100644 index 00000000..25692d85 --- /dev/null +++ b/src/libuv/test/test-emfile.c @@ -0,0 +1,108 @@ +/* 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. + */ + +#if !defined(_WIN32) + +#include "uv.h" +#include "task.h" + +#include +#include +#include +#include + +static void connection_cb(uv_stream_t* server_handle, int status); +static void connect_cb(uv_connect_t* req, int status); + +static const int maxfd = 31; +static unsigned connect_cb_called; +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; + + +TEST_IMPL(emfile) { + struct sockaddr_in addr; + struct rlimit limits; + uv_connect_t connect_req; + uv_loop_t* loop; + int first_fd; + + loop = uv_default_loop(); + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_bind(&server_handle, addr)); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 8, connection_cb)); + + /* Lower the file descriptor limit and use up all fds save one. */ + limits.rlim_cur = limits.rlim_max = maxfd + 1; + if (setrlimit(RLIMIT_NOFILE, &limits)) { + perror("setrlimit(RLIMIT_NOFILE)"); + ASSERT(0); + } + + /* Remember the first one so we can clean up afterwards. */ + do + first_fd = dup(0); + while (first_fd == -1 && errno == EINTR); + ASSERT(first_fd > 0); + + while (dup(0) != -1 || errno == EINTR); + ASSERT(errno == EMFILE); + close(maxfd); + + /* Now connect and use up the last available file descriptor. The EMFILE + * handling logic in src/unix/stream.c should ensure that connect_cb() runs + * whereas connection_cb() should *not* run. + */ + ASSERT(0 == uv_tcp_connect(&connect_req, &client_handle, addr, connect_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT(1 == connect_cb_called); + + /* Close the dups again. Ignore errors in the unlikely event that the + * file descriptors were not contiguous. + */ + while (first_fd < maxfd) { + close(first_fd); + first_fd += 1; + } + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void connection_cb(uv_stream_t* server_handle, int status) { + ASSERT(0 && "connection_cb should not be called."); +} + + +static void connect_cb(uv_connect_t* req, int status) { + /* |status| should equal 0 because the connection should have been accepted, + * it's just that the server immediately closes it again. + */ + ASSERT(0 == status); + connect_cb_called += 1; + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); +} + +#endif /* !defined(_WIN32) */ diff --git a/src/libuv/test/test-ipc.c b/src/libuv/test/test-ipc.c index 40a48b73..ef27cc77 100644 --- a/src/libuv/test/test-ipc.c +++ b/src/libuv/test/test-ipc.c @@ -439,7 +439,7 @@ static void on_tcp_child_process_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t return; } - printf("error recving on tcp connection: %s\n", + printf("error recving on tcp connection: %s\n", uv_strerror(uv_last_error(tcp->loop))); abort(); } diff --git a/src/libuv/test/test-list.h b/src/libuv/test/test-list.h index 7a9b1a29..47444258 100644 --- a/src/libuv/test/test-list.h +++ b/src/libuv/test/test-list.h @@ -64,6 +64,7 @@ TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) +TEST_DECLARE (tcp_close_accept) TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) @@ -87,11 +88,13 @@ TEST_DECLARE (pipe_bind_error_inval) TEST_DECLARE (pipe_listen_without_bind) TEST_DECLARE (pipe_connect_bad_name) TEST_DECLARE (pipe_connect_to_file) +TEST_DECLARE (pipe_server_close) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) TEST_DECLARE (shutdown_close_tcp) TEST_DECLARE (shutdown_close_pipe) TEST_DECLARE (shutdown_eof) +TEST_DECLARE (shutdown_twice) TEST_DECLARE (callback_stack) TEST_DECLARE (error_message) TEST_DECLARE (timer) @@ -106,6 +109,7 @@ TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) TEST_DECLARE (get_loadavg) TEST_DECLARE (walk_handles) +TEST_DECLARE (watcher_cross_stop) TEST_DECLARE (ref) TEST_DECLARE (idle_ref) TEST_DECLARE (async_ref) @@ -157,7 +161,9 @@ TEST_DECLARE (spawn_preserve_env) TEST_DECLARE (spawn_setuid_fails) TEST_DECLARE (spawn_setgid_fails) 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 (fs_poll) TEST_DECLARE (kill) TEST_DECLARE (fs_file_noent) @@ -209,6 +215,7 @@ TEST_DECLARE (poll_duplex) TEST_DECLARE (poll_unidirectional) TEST_DECLARE (poll_close) #ifdef _WIN32 +TEST_DECLARE (poll_closesocket) TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) TEST_DECLARE (argument_escaping) TEST_DECLARE (environment_creation) @@ -216,13 +223,16 @@ TEST_DECLARE (listen_with_simultaneous_accepts) TEST_DECLARE (listen_no_simultaneous_accepts) TEST_DECLARE (fs_stat_root) #else +TEST_DECLARE (emfile) TEST_DECLARE (spawn_setuid_setgid) TEST_DECLARE (we_get_signal) TEST_DECLARE (we_get_signals) TEST_DECLARE (signal_multiple_loops) +TEST_DECLARE (closed_fd_events) #endif #ifdef __APPLE__ TEST_DECLARE (osx_select) +TEST_DECLARE (osx_select_many_fds) #endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) @@ -254,6 +264,7 @@ TASK_LIST_START TEST_ENTRY (pipe_connect_bad_name) TEST_ENTRY (pipe_connect_to_file) + TEST_ENTRY (pipe_server_close) TEST_ENTRY (tty) TEST_ENTRY (stdio_over_pipes) TEST_ENTRY (ipc_listen_before_write) @@ -297,6 +308,7 @@ TASK_LIST_START TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) + TEST_ENTRY (tcp_close_accept) TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) @@ -337,6 +349,9 @@ TASK_LIST_START TEST_ENTRY (shutdown_eof) TEST_HELPER (shutdown_eof, tcp4_echo_server) + TEST_ENTRY (shutdown_twice) + TEST_HELPER (shutdown_twice, tcp4_echo_server) + TEST_ENTRY (callback_stack) TEST_HELPER (callback_stack, tcp4_echo_server) @@ -385,6 +400,8 @@ TASK_LIST_START TEST_ENTRY (loop_handles) TEST_ENTRY (walk_handles) + TEST_ENTRY (watcher_cross_stop) + TEST_ENTRY (active) TEST_ENTRY (embed) @@ -427,11 +444,14 @@ TASK_LIST_START TEST_ENTRY (spawn_setuid_fails) TEST_ENTRY (spawn_setgid_fails) TEST_ENTRY (spawn_stdout_to_file) + TEST_ENTRY (spawn_stdout_and_stderr_to_file) TEST_ENTRY (spawn_auto_unref) + TEST_ENTRY (spawn_closed_process_io) TEST_ENTRY (fs_poll) TEST_ENTRY (kill) #ifdef _WIN32 + TEST_ENTRY (poll_closesocket) TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) TEST_ENTRY (argument_escaping) TEST_ENTRY (environment_creation) @@ -439,14 +459,17 @@ TASK_LIST_START TEST_ENTRY (listen_no_simultaneous_accepts) TEST_ENTRY (fs_stat_root) #else + TEST_ENTRY (emfile) TEST_ENTRY (spawn_setuid_setgid) TEST_ENTRY (we_get_signal) TEST_ENTRY (we_get_signals) TEST_ENTRY (signal_multiple_loops) + TEST_ENTRY (closed_fd_events) #endif #ifdef __APPLE__ TEST_ENTRY (osx_select) + TEST_ENTRY (osx_select_many_fds) #endif TEST_ENTRY (fs_file_noent) diff --git a/src/libuv/test/test-osx-select.c b/src/libuv/test/test-osx-select.c index bf4c3952..249614a1 100644 --- a/src/libuv/test/test-osx-select.c +++ b/src/libuv/test/test-osx-select.c @@ -79,4 +79,51 @@ TEST_IMPL(osx_select) { return 0; } + +TEST_IMPL(osx_select_many_fds) { + int r; + int fd; + size_t i; + size_t len; + const char* str; + struct sockaddr_in addr; + uv_tty_t tty; + uv_tcp_t tcps[1500]; + + addr = uv_ip4_addr("127.0.0.1", 0); + + for (i = 0; i < ARRAY_SIZE(tcps); i++) { + r = uv_tcp_init(uv_default_loop(), &tcps[i]); + ASSERT(r == 0); + r = uv_tcp_bind(&tcps[i], addr); + ASSERT(r == 0); + uv_unref((uv_handle_t*) &tcps[i]); + } + + fd = open("/dev/tty", O_RDONLY); + ASSERT(fd >= 0); + + r = uv_tty_init(uv_default_loop(), &tty, fd, 1); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); + ASSERT(r == 0); + + /* Emulate user-input */ + str = "got some input\n" + "with a couple of lines\n" + "feel pretty happy\n"; + for (i = 0, len = strlen(str); i < len; i++) { + r = ioctl(fd, TIOCSTI, str + i); + ASSERT(r == 0); + } + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(read_count == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + #endif /* __APPLE__ */ diff --git a/src/libuv/test/test-pipe-server-close.c b/src/libuv/test/test-pipe-server-close.c new file mode 100644 index 00000000..1dcdfffa --- /dev/null +++ b/src/libuv/test/test-pipe-server-close.c @@ -0,0 +1,91 @@ +/* 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 + + +static uv_pipe_t pipe_client; +static uv_pipe_t pipe_server; +static uv_connect_t connect_req; + +static int pipe_close_cb_called = 0; +static int pipe_client_connect_cb_called = 0; + + +static void pipe_close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*) &pipe_client || + handle == (uv_handle_t*) &pipe_server); + pipe_close_cb_called++; +} + + +static void pipe_client_connect_cb(uv_connect_t* req, int status) { + ASSERT(req == &connect_req); + ASSERT(status == 0); + + pipe_client_connect_cb_called++; + + uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); + uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); +} + + +static void pipe_server_connection_cb(uv_stream_t* handle, int status) { + /* This function *may* be called, depending on whether accept or the + * connection callback is called first. + */ + ASSERT(status == 0); +} + + +TEST_IMPL(pipe_server_close) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + r = uv_pipe_init(loop, &pipe_server, 0); + ASSERT(r == 0); + + r = uv_pipe_bind(&pipe_server, TEST_PIPENAME); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb); + ASSERT(r == 0); + + r = uv_pipe_init(loop, &pipe_client, 0); + ASSERT(r == 0); + + uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + ASSERT(pipe_client_connect_cb_called == 1); + ASSERT(pipe_close_cb_called == 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/src/libuv/test/test-poll-close.c b/src/libuv/test/test-poll-close.c index 70b775f2..2eccddf5 100644 --- a/src/libuv/test/test-poll-close.c +++ b/src/libuv/test/test-poll-close.c @@ -59,7 +59,7 @@ TEST_IMPL(poll_close) { uv_poll_init_socket(uv_default_loop(), &poll_handles[i], sockets[i]); uv_poll_start(&poll_handles[i], UV_READABLE | UV_WRITABLE, NULL); } - + for (i = 0; i < NUM_SOCKETS; i++) { uv_close((uv_handle_t*) &poll_handles[i], close_cb); } diff --git a/src/libuv/test/test-poll-closesocket.c b/src/libuv/test/test-poll-closesocket.c new file mode 100644 index 00000000..4db74a01 --- /dev/null +++ b/src/libuv/test/test-poll-closesocket.c @@ -0,0 +1,89 @@ +/* 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. + */ + +#ifdef _WIN32 + +#include + +#include "uv.h" +#include "task.h" + +uv_os_sock_t sock; +uv_poll_t handle; + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* h) { + close_cb_called++; +} + + +static void poll_cb(uv_poll_t* h, int status, int events) { + int r; + + ASSERT(status == 0); + ASSERT(h == &handle); + + r = uv_poll_start(&handle, UV_READABLE, poll_cb); + ASSERT(r == 0); + + closesocket(sock); + uv_close((uv_handle_t*) &handle, close_cb); + +} + + +TEST_IMPL(poll_closesocket) { + struct WSAData wsa_data; + int r; + unsigned long on; + struct sockaddr_in addr; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + + sock = socket(AF_INET, SOCK_STREAM, 0); + ASSERT(sock != INVALID_SOCKET); + on = 1; + r = ioctlsocket(sock, FIONBIO, &on); + ASSERT(r == 0); + + r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); + ASSERT(r == 0); + + r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); + ASSERT(r != 0); + ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); + + r = uv_poll_init_socket(uv_default_loop(), &handle, sock); + ASSERT(r == 0); + r = uv_poll_start(&handle, UV_WRITABLE, poll_cb); + ASSERT(r == 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif diff --git a/src/libuv/test/test-shutdown-twice.c b/src/libuv/test/test-shutdown-twice.c new file mode 100644 index 00000000..7ce3e04a --- /dev/null +++ b/src/libuv/test/test-shutdown-twice.c @@ -0,0 +1,83 @@ +/* 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 is a regression test for issue #1113 (calling uv_shutdown twice will + * leave a ghost request in the system) + */ + +#include "uv.h" +#include "task.h" + +static uv_shutdown_t req1; +static uv_shutdown_t req2; + +static int shutdown_cb_called = 0; + +static void close_cb(uv_handle_t* handle) { + +} + +static void shutdown_cb(uv_shutdown_t* req, int status) { + ASSERT(req == &req1); + ASSERT(status == 0); + shutdown_cb_called++; + uv_close((uv_handle_t*) req->handle, close_cb); +} + +static void connect_cb(uv_connect_t* req, int status) { + int r; + + ASSERT(status == 0); + + r = uv_shutdown(&req1, req->handle, shutdown_cb); + ASSERT(r == 0); + r = uv_shutdown(&req2, req->handle, shutdown_cb); + ASSERT(r != 0); + +} + +TEST_IMPL(shutdown_twice) { + struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + uv_loop_t* loop; + int r; + uv_tcp_t h; + + uv_connect_t connect_req; + + loop = uv_default_loop(); + + r = uv_tcp_init(loop, &h); + + r = uv_tcp_connect(&connect_req, + &h, + addr, + connect_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(shutdown_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/src/libuv/test/test-spawn.c b/src/libuv/test/test-spawn.c index 56eca9c5..43da6664 100644 --- a/src/libuv/test/test-spawn.c +++ b/src/libuv/test/test-spawn.c @@ -40,6 +40,7 @@ static char exepath[1024]; static size_t exepath_size = 1024; static char* args[3]; static int no_term_signal; +static int timer_counter; #define OUTPUT_SIZE 1024 static char output[OUTPUT_SIZE]; @@ -118,6 +119,12 @@ static void on_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { } +static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t buf) { + uv_read_stop(tcp); + on_read(tcp, nread, buf); +} + + static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); uv_close((uv_handle_t*)req->handle, close_cb); @@ -145,6 +152,11 @@ static void timer_cb(uv_timer_t* handle, int status) { } +static void timer_counter_cb(uv_timer_t* handle, int status) { + ++timer_counter; +} + + TEST_IMPL(spawn_fails) { init_process_options("", exit_cb_failure_expected); options.file = options.args[0] = "program-that-had-better-not-exist"; @@ -263,6 +275,61 @@ TEST_IMPL(spawn_stdout_to_file) { } +TEST_IMPL(spawn_stdout_and_stderr_to_file) { + int r; + uv_file file; + uv_fs_t fs_req; + uv_stdio_container_t stdio[3]; + + /* Setup. */ + unlink("stdout_file"); + + init_process_options("spawn_helper6", exit_cb); + + r = uv_fs_open(uv_default_loop(), &fs_req, "stdout_file", O_CREAT | O_RDWR, + S_IREAD | S_IWRITE, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&fs_req); + + file = r; + + options.stdio = stdio; + options.stdio[0].flags = UV_IGNORE; + options.stdio[1].flags = UV_INHERIT_FD; + options.stdio[1].data.fd = file; + options.stdio[2].flags = UV_INHERIT_FD; + options.stdio[2].data.fd = file; + options.stdio_count = 3; + + 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); + + r = uv_fs_read(uv_default_loop(), &fs_req, file, output, sizeof(output), + 0, NULL); + ASSERT(r == 27); + uv_fs_req_cleanup(&fs_req); + + r = uv_fs_close(uv_default_loop(), &fs_req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&fs_req); + + printf("output is: %s", output); + ASSERT(strcmp("hello world\nhello errworld\n", output) == 0); + + /* Cleanup. */ + unlink("stdout_file"); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + TEST_IMPL(spawn_stdin) { int r; uv_pipe_t out; @@ -562,6 +629,86 @@ TEST_IMPL(spawn_and_ping) { } +TEST_IMPL(spawn_same_stdout_stderr) { + uv_write_t write_req; + uv_pipe_t in, out; + uv_buf_t buf; + uv_stdio_container_t stdio[3]; + int r; + + init_process_options("spawn_helper3", exit_cb); + buf = uv_buf_init("TEST", 4); + + uv_pipe_init(uv_default_loop(), &out, 0); + uv_pipe_init(uv_default_loop(), &in, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio_count = 2; + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + /* Sending signum == 0 should check if the + * child process is still alive, not kill it. + */ + r = uv_process_kill(&process, 0); + ASSERT(r == 0); + + r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(strcmp(output, "TEST") == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(spawn_closed_process_io) { + uv_pipe_t in; + uv_write_t write_req; + uv_buf_t buf; + uv_stdio_container_t stdio[2]; + static char buffer[] = "hello-from-spawn_stdin\n"; + + init_process_options("spawn_helper3", exit_cb); + + uv_pipe_init(uv_default_loop(), &in, 0); + options.stdio = stdio; + options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + options.stdio[0].data.stream = (uv_stream_t*) ∈ + options.stdio_count = 1; + + close(0); /* Close process stdin. */ + + ASSERT(0 == uv_spawn(uv_default_loop(), &process, options)); + + buf.base = buffer; + buf.len = sizeof(buffer); + ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* process, child stdin */ + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + TEST_IMPL(kill) { int r; uv_err_t err; @@ -936,3 +1083,65 @@ TEST_IMPL(spawn_auto_unref) { MAKE_VALGRIND_HAPPY(); return 0; } + + +#ifndef _WIN32 +TEST_IMPL(closed_fd_events) { + uv_stdio_container_t stdio[3]; + uv_pipe_t pipe_handle; + int fd[2]; + + /* create a pipe and share it with a child process */ + ASSERT(0 == pipe(fd)); + ASSERT(0 == fcntl(fd[0], F_SETFL, O_NONBLOCK)); + + /* spawn_helper4 blocks indefinitely. */ + init_process_options("spawn_helper4", exit_cb); + options.stdio_count = 3; + options.stdio = stdio; + options.stdio[0].data.fd = fd[0]; + options.stdio[0].flags = UV_INHERIT_FD; + options.stdio[1].flags = UV_IGNORE; + options.stdio[2].flags = UV_IGNORE; + + ASSERT(0 == uv_spawn(uv_default_loop(), &process, options)); + uv_unref((uv_handle_t*) &process); + + /* read from the pipe with uv */ + ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); + ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + fd[0] = -1; + + ASSERT(0 == uv_read_start((uv_stream_t*) &pipe_handle, on_alloc, on_read_once)); + + ASSERT(1 == write(fd[1], "", 1)); + + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + + /* should have received just one byte */ + ASSERT(output_used == 1); + + /* close the pipe and see if we still get events */ + uv_close((uv_handle_t*) &pipe_handle, close_cb); + + ASSERT(1 == write(fd[1], "", 1)); + + ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); + ASSERT(0 == uv_timer_start(&timer, timer_counter_cb, 10, 0)); + + /* see if any spurious events interrupt the timer */ + if (1 == uv_run(uv_default_loop(), UV_RUN_ONCE)) { + if (1 == uv_run(uv_default_loop(), UV_RUN_ONCE)) + ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + } + + ASSERT(timer_counter == 1); + + /* cleanup */ + ASSERT(0 == uv_process_kill(&process, /* SIGTERM */ 15)); + ASSERT(0 == close(fd[1])); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif /* !_WIN32 */ diff --git a/src/libuv/test/test-tcp-close-accept.c b/src/libuv/test/test-tcp-close-accept.c new file mode 100644 index 00000000..f5336a58 --- /dev/null +++ b/src/libuv/test/test-tcp-close-accept.c @@ -0,0 +1,177 @@ +/* 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 + +static struct sockaddr_in addr; +static uv_tcp_t tcp_server; +static uv_tcp_t tcp_outgoing[2]; +static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)]; +static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)]; +static uv_tcp_t tcp_check; +static uv_connect_t tcp_check_req; +static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)]; +static unsigned int got_connections; +static unsigned int close_cb_called; +static unsigned int write_cb_called; +static unsigned int read_cb_called; + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + write_cb_called++; +} + +static void connect_cb(uv_connect_t* req, int status) { + unsigned int i; + uv_buf_t buf; + uv_stream_t* outgoing; + + if (req == &tcp_check_req) { + ASSERT(status != 0); + + /* Close check and incoming[0], time to finish test */ + uv_close((uv_handle_t*) &tcp_incoming[0], close_cb); + uv_close((uv_handle_t*) &tcp_check, close_cb); + return; + } + + ASSERT(status == 0); + ASSERT(connect_reqs <= req); + ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs)); + i = req - connect_reqs; + + buf = uv_buf_init("x", 1); + outgoing = (uv_stream_t*) &tcp_outgoing[i]; + ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); +} + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char buf[1]; + + return uv_buf_init(buf, sizeof(buf)); +} + +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t b) { + uv_loop_t* loop; + unsigned int i; + + /* Only first stream should receive read events */ + ASSERT(stream == (uv_stream_t*) &tcp_incoming[0]); + ASSERT(0 == uv_read_stop(stream)); + ASSERT(1 == nread); + + loop = stream->loop; + read_cb_called++; + + /* Close all active incomings, except current one */ + for (i = 1; i < got_connections; i++) + uv_close((uv_handle_t*) &tcp_incoming[i], close_cb); + + /* Create new fd that should be one of the closed incomings */ + ASSERT(0 == uv_tcp_init(loop, &tcp_check)); + ASSERT(0 == uv_tcp_connect(&tcp_check_req, &tcp_check, addr, connect_cb)); + ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); + + /* Close server, so no one will connect to it */ + uv_close((uv_handle_t*) &tcp_server, close_cb); +} + +static void connection_cb(uv_stream_t* server, int status) { + unsigned int i; + uv_tcp_t* incoming; + + ASSERT(server == (uv_stream_t*) &tcp_server); + + /* Ignore tcp_check connection */ + if (got_connections == ARRAY_SIZE(tcp_incoming)) + return; + + /* Accept everyone */ + incoming = &tcp_incoming[got_connections++]; + ASSERT(0 == uv_tcp_init(server->loop, incoming)); + ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming)); + + if (got_connections != ARRAY_SIZE(tcp_incoming)) + return; + + /* Once all clients are accepted - start reading */ + for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) { + incoming = &tcp_incoming[i]; + ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); + } +} + +TEST_IMPL(tcp_close_accept) { + unsigned int i; + uv_loop_t* loop; + uv_tcp_t* client; + + /* + * A little explanation of what goes on below: + * + * We'll create server and connect to it using two clients, each writing one + * byte once connected. + * + * When all clients will be accepted by server - we'll start reading from them + * and, on first client's first byte, will close second client and server. + * After that, we'll immediately initiate new connection to server using + * tcp_check handle (thus, reusing fd from second client). + * + * In this situation uv__io_poll()'s event list should still contain read + * event for second client, and, if not cleaned up properly, `tcp_check` will + * receive stale event of second incoming and invoke `connect_cb` with zero + * status. + */ + + loop = uv_default_loop(); + addr = uv_ip4_addr("0.0.0.0", TEST_PORT); + + ASSERT(0 == uv_tcp_init(loop, &tcp_server)); + ASSERT(0 == uv_tcp_bind(&tcp_server, addr)); + ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server, + ARRAY_SIZE(tcp_outgoing), + connection_cb)); + + for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) { + client = tcp_outgoing + i; + + ASSERT(0 == uv_tcp_init(loop, client)); + ASSERT(0 == uv_tcp_connect(&connect_reqs[i], client, addr, connect_cb)); + } + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections); + ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called); + ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called); + ASSERT(1 == read_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/src/libuv/test/test-watcher-cross-stop.c b/src/libuv/test/test-watcher-cross-stop.c new file mode 100644 index 00000000..5b53320b --- /dev/null +++ b/src/libuv/test/test-watcher-cross-stop.c @@ -0,0 +1,108 @@ +/* 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 + +#if !defined(_WIN32) +#include +#include /* setrlimit() */ +#endif + +/* NOTE: Number should be big enough to trigger this problem */ +static uv_udp_t sockets[2500]; +static uv_udp_send_t reqs[ARRAY_SIZE(sockets)]; +static char buf[1]; +static unsigned int recv_cb_called; +static unsigned int send_cb_called; +static unsigned int close_cb_called; + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + return uv_buf_init(buf, sizeof(buf)); +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + recv_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + send_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +TEST_IMPL(watcher_cross_stop) { + uv_loop_t* loop = uv_default_loop(); + unsigned int i; + struct sockaddr_in addr; + uv_buf_t buf; + char big_string[1024]; + +#if !defined(_WIN32) + { + struct rlimit lim; + lim.rlim_cur = ARRAY_SIZE(sockets) + 32; + lim.rlim_max = ARRAY_SIZE(sockets) + 32; + if (setrlimit(RLIMIT_NOFILE, &lim)) + RETURN_SKIP("File descriptor limit too low."); + } +#endif + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + memset(big_string, 'A', sizeof(big_string)); + buf = uv_buf_init(big_string, sizeof(big_string)); + + for (i = 0; i < ARRAY_SIZE(sockets); i++) { + ASSERT(0 == uv_udp_init(loop, &sockets[i])); + ASSERT(0 == uv_udp_bind(&sockets[i], addr, 0)); + ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb)); + ASSERT(0 == uv_udp_send(&reqs[i], &sockets[i], &buf, 1, addr, send_cb)); + } + + while (recv_cb_called == 0) + uv_run(loop, UV_RUN_ONCE); + + 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(ARRAY_SIZE(sockets) == send_cb_called); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT(ARRAY_SIZE(sockets) == close_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/src/libuv/uv.gyp b/src/libuv/uv.gyp index f61ebb53..5af99bd5 100644 --- a/src/libuv/uv.gyp +++ b/src/libuv/uv.gyp @@ -194,6 +194,7 @@ }, 'defines': [ '_DARWIN_USE_64_BIT_INODE=1', + '_DARWIN_UNLIMITED_SELECT=1', ] }], [ 'OS!="mac"', { @@ -295,6 +296,7 @@ 'test/test-delayed-accept.c', 'test/test-error.c', 'test/test-embed.c', + 'test/test-emfile.c', 'test/test-fail-always.c', 'test/test-fs.c', 'test/test-fs-event.c', @@ -310,15 +312,18 @@ 'test/test-loop-handles.c', 'test/test-loop-stop.c', 'test/test-walk-handles.c', + 'test/test-watcher-cross-stop.c', 'test/test-multiple-listen.c', 'test/test-osx-select.c', 'test/test-pass-always.c', 'test/test-ping-pong.c', 'test/test-pipe-bind-error.c', 'test/test-pipe-connect-error.c', + 'test/test-pipe-server-close.c', 'test/test-platform-output.c', 'test/test-poll.c', 'test/test-poll-close.c', + 'test/test-poll-closesocket.c', 'test/test-process-title.c', 'test/test-ref.c', 'test/test-run-nowait.c', @@ -326,6 +331,7 @@ 'test/test-semaphore.c', 'test/test-shutdown-close.c', 'test/test-shutdown-eof.c', + 'test/test-shutdown-twice.c', 'test/test-signal.c', 'test/test-signal-multiple-loops.c', 'test/test-spawn.c', @@ -334,6 +340,7 @@ 'test/test-tcp-bind-error.c', 'test/test-tcp-bind6-error.c', 'test/test-tcp-close.c', + 'test/test-tcp-close-accept.c', 'test/test-tcp-close-while-connecting.c', 'test/test-tcp-connect-error-after-write.c', 'test/test-tcp-shutdown-after-write.c', diff --git a/src/libuv/vcbuild.bat b/src/libuv/vcbuild.bat index 1b2f865a..42b0b947 100644 --- a/src/libuv/vcbuild.bat +++ b/src/libuv/vcbuild.bat @@ -33,7 +33,7 @@ if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok if /i "%1"=="nobuild" set nobuild=1&goto arg-ok if /i "%1"=="x86" set target_arch=ia32&set platform=WIN32&set vs_toolset=x86&goto arg-ok if /i "%1"=="ia32" set target_arch=ia32&set platform=WIN32&set vs_toolset=x86&goto arg-ok -if /i "%1"=="x64" set target_arch=x64&set platform=amd64&set vs_toolset=x64&goto arg-ok +if /i "%1"=="x64" set target_arch=x64&set platform=x64&set vs_toolset=x64&goto arg-ok if /i "%1"=="shared" set library=shared_library&goto arg-ok if /i "%1"=="static" set library=static_library&goto arg-ok :arg-ok @@ -41,6 +41,17 @@ shift goto next-arg :args-done +if defined WindowsSDKDir goto select-target +if defined VCINSTALLDIR goto select-target + +@rem Look for Visual Studio 2013 +if not defined VS120COMNTOOLS goto vc-set-2012 +if not exist "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2012 +call "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% +set GYP_MSVS_VERSION=2013 +goto select-target + +:vc-set-2012 @rem Look for Visual Studio 2012 if not defined VS110COMNTOOLS goto vc-set-2010 if not exist "%VS110COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2010 @@ -91,7 +102,7 @@ exit /b 1 :have_gyp if not defined PYTHON set PYTHON="python" -%PYTHON% gyp_uv -Dtarget_arch=%target_arch% -Dlibrary=%library% +%PYTHON% gyp_uv.py -Dtarget_arch=%target_arch% -Dlibrary=%library% if errorlevel 1 goto create-msvs-files-failed if not exist uv.sln goto create-msvs-files-failed echo Project files generated. @@ -101,10 +112,8 @@ echo Project files generated. if defined nobuild goto run @rem Check if VS build env is available -if not defined VCINSTALLDIR goto msbuild-not-found -goto msbuild-found - -:msbuild-not-found +if defined VCINSTALLDIR goto msbuild-found +if defined WindowsSDKDir goto msbuild-found echo Build skipped. To build, this file needs to run from VS cmd prompt. goto run