diff --git a/AUTHORS b/AUTHORS index 32793e19..1562f4a6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -84,3 +84,4 @@ Ben Kelly Kristian Evensen Nils Maier Nicholas Vavilov +Miroslav Bajtoš diff --git a/ChangeLog b/ChangeLog index 03172c63..cdd9b23a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,39 +1,16 @@ -2013.04.11, Version 0.11.1 (Unstable), 5c10e82ae0bc99eff86d4b9baff1f1aa0bf84c0a +2013.04.24, Version 0.10.5 (Stable), 6595a7732c52eb4f8e57c88655f72997a8567a67 -This is the first versioned release from the current unstable libuv branch. +Changes since version 0.10.4: -Changes since Node.js v0.11.0: +* unix: silence STATIC_ASSERT compiler warnings (Ben Noordhuis) -* all platforms: nanosecond resolution support for uv_fs_[fl]stat (Timothy J. - Fontaine) +* windows: make timers handle large timeouts (Miroslav Bajtoš) -* all platforms: add netmask to uv_interface_address (Ben Kelly) +* windows: remove superfluous assert statement (Bert Belder) -* unix: make sure the `status` parameter passed to the `uv_getaddrinfo` is 0 or - -1 (Ben Noordhuis) +* unix: silence STATIC_ASSERT compiler warnings (Ben Noordhuis) -* unix: limit the number of iovecs written in a single `writev` syscall to - IOV_MAX (Fedor Indutny) - -* unix: add dtrace probes for tick-start and tick-stop (Timothy J. Fontaine) - -* mingw-w64: don't call _set_invalid_parameter_handler (Nils Maier) - -* windows: fix memory leak in fs__sendfile (Shannen Saez) - -* windows: fix edge case bugs in uv_cpu_info (Bert Belder) - -* include: no longer ship with / include ngx-queue.h (Ben Noordhuis) - -* include: remove UV_VERSION_* macros from uv.h (Ben Noordhuis) - -* documentation updates (Kristian Evensen, Ben Kelly, Ben Noordhuis) - -* build: fix dtrace-enabled builds (Ben Noordhuis, Timothy J. Fontaine) - -* build: gyp disable thin archives (Timothy J. Fontaine) - -* build: add support for Visual Studio 2012 (Nicholas Vavilov) +* linux: don't use fopen() in uv_resident_set_memory() (Ben Noordhuis) 2013.04.12, Version 0.10.4 (Stable), 85827e26403ac6dfa331af8ec9916ea7e27bd833 @@ -71,6 +48,42 @@ Changes since version 0.10.3: * build: -Wno-dollar-in-identifier-extension is clang only (Ben Noordhuis) +2013.04.11, Version 0.11.1 (Unstable), 5c10e82ae0bc99eff86d4b9baff1f1aa0bf84c0a + +This is the first versioned release from the current unstable libuv branch. + +Changes since Node.js v0.11.0: + +* all platforms: nanosecond resolution support for uv_fs_[fl]stat (Timothy J. + Fontaine) + +* all platforms: add netmask to uv_interface_address (Ben Kelly) + +* unix: make sure the `status` parameter passed to the `uv_getaddrinfo` is 0 or + -1 (Ben Noordhuis) + +* unix: limit the number of iovecs written in a single `writev` syscall to + IOV_MAX (Fedor Indutny) + +* unix: add dtrace probes for tick-start and tick-stop (Timothy J. Fontaine) + +* mingw-w64: don't call _set_invalid_parameter_handler (Nils Maier) + +* windows: fix memory leak in fs__sendfile (Shannen Saez) + +* windows: fix edge case bugs in uv_cpu_info (Bert Belder) + +* include: no longer ship with / include ngx-queue.h (Ben Noordhuis) + +* include: remove UV_VERSION_* macros from uv.h (Ben Noordhuis) + +* documentation updates (Kristian Evensen, Ben Kelly, Ben Noordhuis) + +* build: fix dtrace-enabled builds (Ben Noordhuis, Timothy J. Fontaine) + +* build: gyp disable thin archives (Timothy J. Fontaine) + +* build: add support for Visual Studio 2012 (Nicholas Vavilov) 2013.02.04, Version 0.10.3 (Stable), 31ebe23973dd98fd8a24c042b606f37a794e99d0 Changes since version 0.10.2: diff --git a/build.mk b/build.mk index 00c71a53..d3a9046b 100644 --- a/build.mk +++ b/build.mk @@ -88,6 +88,7 @@ TESTS= \ test/test-loop-stop.o \ test/test-multiple-listen.o \ test/test-mutexes.o \ + test/test-osx-select.o \ test/test-pass-always.o \ test/test-ping-pong.o \ test/test-pipe-bind-error.o \ diff --git a/src/unix/core.c b/src/unix/core.c index 9268f40c..98b48989 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -71,10 +71,8 @@ STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->base) == sizeof(((struct iovec*) 0)->iov_base)); STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->len) == sizeof(((struct iovec*) 0)->iov_len)); -STATIC_ASSERT((uintptr_t) &((uv_buf_t*) 0)->base == - (uintptr_t) &((struct iovec*) 0)->iov_base); -STATIC_ASSERT((uintptr_t) &((uv_buf_t*) 0)->len == - (uintptr_t) &((struct iovec*) 0)->iov_len); +STATIC_ASSERT(offsetof(uv_buf_t, base) == offsetof(struct iovec, iov_base)); +STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len)); uint64_t uv_hrtime(void) { diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 630f5a1c..7bb05b5d 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -284,98 +284,59 @@ uint64_t uv_get_total_memory(void) { uv_err_t uv_resident_set_memory(size_t* rss) { - FILE* f; - int itmp; - char ctmp; - unsigned int utmp; - size_t page_size = getpagesize(); - char *cbuf; - int foundExeEnd; - char buf[PATH_MAX + 1]; + char buf[1024]; + const char* s; + ssize_t n; + long val; + int fd; + int i; - f = fopen("/proc/self/stat", "r"); - if (!f) return uv__new_sys_error(errno); + do + fd = open("/proc/self/stat", O_RDONLY); + while (fd == -1 && errno == EINTR); - /* PID */ - if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Exec file */ - cbuf = buf; - foundExeEnd = 0; - if (fscanf (f, "%c", cbuf++) == 0) goto error; - while (1) { - if (fscanf(f, "%c", cbuf) == 0) goto error; - if (*cbuf == ')') { - foundExeEnd = 1; - } else if (foundExeEnd && *cbuf == ' ') { - *cbuf = 0; - break; - } + if (fd == -1) + return uv__new_sys_error(errno); - cbuf++; + do + n = read(fd, buf, sizeof(buf) - 1); + while (n == -1 && errno == EINTR); + + SAVE_ERRNO(close(fd)); + if (n == -1) + return uv__new_sys_error(errno); + buf[n] = '\0'; + + s = strchr(buf, ' '); + if (s == NULL) + goto err; + + s += 1; + if (*s != '(') + goto err; + + s = strchr(s, ')'); + if (s == NULL) + goto err; + + for (i = 1; i <= 22; i++) { + s = strchr(s + 1, ' '); + if (s == NULL) + goto err; } - /* State */ - if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */ - /* Parent process */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Process group */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Session id */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* TTY */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* TTY owner process group */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Flags */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* Minor faults (no memory page) */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* Minor faults, children */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* Major faults (memory page faults) */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* Major faults, children */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* utime */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* stime */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* utime, children */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* stime, children */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* jiffies remaining in current time slice */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* 'nice' value */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* jiffies until next timeout */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* jiffies until next SIGALRM */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* start time (jiffies since system boot) */ - if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ - /* Virtual memory size */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ + errno = 0; + val = strtol(s, NULL, 10); + if (errno != 0) + goto err; + if (val < 0) + goto err; - /* Resident set size */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - *rss = (size_t) utmp * page_size; - - /* rlim */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* Start of text */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* End of text */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - /* Start of stack */ - if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */ - - fclose (f); + *rss = val * getpagesize(); return uv_ok_; -error: - fclose (f); - return uv__new_sys_error(errno); +err: + return uv__new_artificial_error(UV_EINVAL); } diff --git a/src/unix/stream.c b/src/unix/stream.c index 7a185ab6..0c38231d 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -46,8 +46,8 @@ typedef struct uv__stream_select_s uv__stream_select_t; struct uv__stream_select_s { uv_stream_t* stream; uv_thread_t thread; - uv_sem_t sem; - uv_mutex_t mutex; + uv_sem_t close_sem; + uv_sem_t async_sem; uv_async_t async; int events; int fake_fd; @@ -139,7 +139,7 @@ static void uv__stream_osx_select(void* arg) { stream = arg; s = stream->select; - fd = stream->io_watcher.fd; + fd = s->fd; if (fd > s->int_fd) max_fd = fd; @@ -148,7 +148,7 @@ static void uv__stream_osx_select(void* arg) { while (1) { /* Terminate on semaphore */ - if (uv_sem_trywait(&s->sem) == 0) + if (uv_sem_trywait(&s->close_sem) == 0) break; /* Watch fd using select(2) */ @@ -202,12 +202,16 @@ static void uv__stream_osx_select(void* arg) { if (FD_ISSET(fd, &swrite)) events |= UV__POLLOUT; - uv_mutex_lock(&s->mutex); - s->events |= events; - uv_mutex_unlock(&s->mutex); + assert(events != 0 || FD_ISSET(s->int_fd, &sread)); + if (events != 0) { + ACCESS_ONCE(int, s->events) = events; - if (events != 0) uv_async_send(&s->async); + uv_sem_wait(&s->async_sem); + + /* Should be processed at this stage */ + assert((s->events == 0) || (stream->flags & UV_CLOSING)); + } } } @@ -240,10 +244,9 @@ static void uv__stream_osx_select_cb(uv_async_t* handle, int status) { stream = s->stream; /* Get and reset stream's events */ - uv_mutex_lock(&s->mutex); events = s->events; - s->events = 0; - uv_mutex_unlock(&s->mutex); + ACCESS_ONCE(int, s->events) = 0; + uv_sem_post(&s->async_sem); assert(events != 0); assert(events == (events & (UV__POLLIN | UV__POLLOUT))); @@ -305,6 +308,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { if (s == NULL) return uv__set_artificial_error(stream->loop, UV_ENOMEM); + s->events = 0; s->fd = *fd; if (uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb)) { @@ -315,10 +319,10 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { s->async.flags |= UV__HANDLE_INTERNAL; uv__handle_unref(&s->async); - if (uv_sem_init(&s->sem, 0)) + if (uv_sem_init(&s->close_sem, 0)) goto fatal1; - if (uv_mutex_init(&s->mutex)) + if (uv_sem_init(&s->async_sem, 0)) goto fatal2; /* Create fds for io watcher and to interrupt the select() loop. */ @@ -343,9 +347,9 @@ fatal4: s->fake_fd = -1; s->int_fd = -1; fatal3: - uv_mutex_destroy(&s->mutex); + uv_sem_destroy(&s->async_sem); fatal2: - uv_sem_destroy(&s->sem); + uv_sem_destroy(&s->close_sem); fatal1: uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); return uv__set_sys_error(stream->loop, errno); @@ -437,7 +441,6 @@ void uv__stream_destroy(uv_stream_t* stream) { */ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { int fd; - int r; if (loop->emfile_fd == -1) return -1; @@ -455,14 +458,8 @@ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { if (errno == EINTR) continue; - if (errno == EAGAIN || errno == EWOULDBLOCK) - r = 0; - else - r = -1; - - loop->emfile_fd = uv__open_cloexec("/", O_RDONLY); - - return r; + SAVE_ERRNO(loop->emfile_fd = uv__open_cloexec("/", O_RDONLY)); + return errno; } } @@ -475,10 +472,9 @@ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { - static int use_emfile_trick = -1; uv_stream_t* stream; + int err; int fd; - int r; stream = container_of(w, uv_stream_t, io_watcher); assert(events == UV__POLLIN); @@ -492,50 +488,32 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { */ while (uv__stream_fd(stream) != -1) { assert(stream->accepted_fd == -1); + #if defined(UV_HAVE_KQUEUE) if (w->rcount <= 0) return; #endif /* defined(UV_HAVE_KQUEUE) */ + fd = uv__accept(uv__stream_fd(stream)); - if (fd == -1) { - switch (errno) { -#if EWOULDBLOCK != EAGAIN - case EWOULDBLOCK: -#endif - case EAGAIN: - return; /* Not an error. */ + if (errno == EAGAIN || errno == EWOULDBLOCK) + return; /* Not an error. */ - case ECONNABORTED: - UV_DEC_BACKLOG(w) - continue; /* Ignore. */ + if (errno == ECONNABORTED) + continue; /* Ignore. Nothing we can do about that. */ - case EMFILE: - case ENFILE: - if (use_emfile_trick == -1) { - const char* val = getenv("UV_ACCEPT_EMFILE_TRICK"); - use_emfile_trick = (val == NULL || atoi(val) != 0); - } - - if (use_emfile_trick) { - SAVE_ERRNO(r = uv__emfile_trick(loop, uv__stream_fd(stream))); - if (r == 0) { - UV_DEC_BACKLOG(w) - continue; - } - } - - /* Fall through. */ - - default: - uv__set_sys_error(loop, errno); - stream->connection_cb(stream, -1); - continue; + if (errno == EMFILE || errno == ENFILE) { + SAVE_ERRNO(err = uv__emfile_trick(loop, uv__stream_fd(stream))); + if (err == EAGAIN || err == EWOULDBLOCK) + break; } + + uv__set_sys_error(loop, errno); + stream->connection_cb(stream, -1); + continue; } UV_DEC_BACKLOG(w) - stream->accepted_fd = fd; stream->connection_cb(stream, 0); @@ -1356,11 +1334,12 @@ void uv__stream_close(uv_stream_t* handle) { s = handle->select; - uv_sem_post(&s->sem); + uv_sem_post(&s->close_sem); + uv_sem_post(&s->async_sem); uv__stream_osx_interrupt_select(handle); uv_thread_join(&s->thread); - uv_sem_destroy(&s->sem); - uv_mutex_destroy(&s->mutex); + uv_sem_destroy(&s->close_sem); + uv_sem_destroy(&s->async_sem); close(s->fake_fd); close(s->int_fd); uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); diff --git a/test/test-list.h b/test/test-list.h index 601ff437..0822d7f1 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -222,6 +222,9 @@ TEST_DECLARE (we_get_signal) TEST_DECLARE (we_get_signals) TEST_DECLARE (signal_multiple_loops) #endif +#ifdef __APPLE__ +TEST_DECLARE (osx_select) +#endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) HELPER_DECLARE (udp4_echo_server) @@ -444,6 +447,10 @@ TASK_LIST_START TEST_ENTRY (signal_multiple_loops) #endif +#ifdef __APPLE__ + TEST_ENTRY (osx_select) +#endif + TEST_ENTRY (fs_file_noent) TEST_ENTRY (fs_file_nametoolong) TEST_ENTRY (fs_file_loop) diff --git a/test/test-osx-select.c b/test/test-osx-select.c new file mode 100644 index 00000000..5c2cbd4d --- /dev/null +++ b/test/test-osx-select.c @@ -0,0 +1,82 @@ +/* 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" + +#ifdef __APPLE__ + +#include +#include + +static int read_count; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { + static char buf[1024]; + + return uv_buf_init(buf, ARRAY_SIZE(buf)); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { + fprintf(stdout, "got data %d\n", ++read_count); + + if (read_count == 3) + uv_close((uv_handle_t*) stream, NULL); +} + + +TEST_IMPL(osx_select) { + int r; + int fd; + size_t i; + size_t len; + const char* str; + uv_tty_t tty; + + fd = open("/dev/tty", O_RDONLY); + + ASSERT(fd >= 0); + + r = uv_tty_init(uv_default_loop(), &tty, fd, 1); + ASSERT(r == 0); + + uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); + + // 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/uv.gyp b/uv.gyp index 1b843e40..7fab2a3d 100644 --- a/uv.gyp +++ b/uv.gyp @@ -236,21 +236,16 @@ }], [ 'OS=="freebsd" or OS=="dragonflybsd"', { 'sources': [ 'src/unix/freebsd.c' ], - 'link_settings': { - 'libraries': [ - '-lkvm', - ], - }, }], [ 'OS=="openbsd"', { 'sources': [ 'src/unix/openbsd.c' ], }], [ 'OS=="netbsd"', { 'sources': [ 'src/unix/netbsd.c' ], + }], + [ 'OS in "freebsd dragonflybsd openbsd netbsd".split()', { 'link_settings': { - 'libraries': [ - '-lkvm', - ], + 'libraries': [ '-lkvm' ], }, }], [ 'OS in "mac freebsd dragonflybsd openbsd netbsd".split()', { @@ -310,6 +305,7 @@ 'test/test-loop-stop.c', 'test/test-walk-handles.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',