diff --git a/deps/uv/README.md b/deps/uv/README.md index ca8fc2e3f1..6789ffcb0c 100644 --- a/deps/uv/README.md +++ b/deps/uv/README.md @@ -77,6 +77,9 @@ Macintosh users run ./gyp_uv -f xcode xcodebuild -project uv.xcodeproj -configuration Release -target All +Note for Linux users: compile your project with `-D_GNU_SOURCE` when you +include `uv.h`. GYP builds take care of that automatically. If you use +autotools, add a `AC_GNU_SOURCE` declaration to your `configure.ac`. ## Supported Platforms diff --git a/deps/uv/config-unix.mk b/deps/uv/config-unix.mk index 53c636de15..c0be7e4a1e 100644 --- a/deps/uv/config-unix.mk +++ b/deps/uv/config-unix.mk @@ -60,6 +60,7 @@ CPPFLAGS += -D_DARWIN_USE_64_BIT_INODE=1 LINKFLAGS+=-framework CoreServices OBJS += src/unix/darwin.o OBJS += src/unix/kqueue.o +OBJS += src/unix/fsevents.o endif ifeq (Linux,$(uname_S)) @@ -91,7 +92,7 @@ endif ifeq (NetBSD,$(uname_S)) EV_CONFIG=config_netbsd.h EIO_CONFIG=config_netbsd.h -LINKFLAGS+= +LINKFLAGS+=-lkvm OBJS += src/unix/netbsd.o OBJS += src/unix/kqueue.o endif diff --git a/deps/uv/gyp_uv b/deps/uv/gyp_uv index a8528fa91d..9c719fde56 100755 --- a/deps/uv/gyp_uv +++ b/deps/uv/gyp_uv @@ -60,9 +60,11 @@ if __name__ == '__main__': # There's a bug with windows which doesn't allow this feature. if sys.platform != 'win32': - args.extend(['--generator-output', output_dir]) - args.extend(['-Goutput_dir=' + output_dir]) - args.extend('-f make'.split()) + if '-f' not in args: + args.extend('-f make'.split()) + if 'ninja' not in args: + args.extend(['-Goutput_dir=' + output_dir]) + args.extend(['--generator-output', output_dir]) (major, minor), is_clang = compiler_version() args.append('-Dgcc_version=%d' % (10 * major + minor)) args.append('-Dclang=%d' % int(is_clang)) diff --git a/deps/uv/include/uv-private/uv-darwin.h b/deps/uv/include/uv-private/uv-darwin.h index 7f1b928a64..397c6a97a4 100644 --- a/deps/uv/include/uv-private/uv-darwin.h +++ b/deps/uv/include/uv-private/uv-darwin.h @@ -29,10 +29,23 @@ # define UV_PLATFORM_SEM_T semaphore_t #endif +#define UV_PLATFORM_LOOP_FIELDS \ + uv_thread_t cf_thread; \ + void* cf_cb; \ + void* cf_loop; \ + uv_mutex_t cf_mutex; \ + uv_sem_t cf_sem; \ + ngx_queue_t cf_signals; \ + #define UV_PLATFORM_FS_EVENT_FIELDS \ ev_io event_watcher; \ int fflags; \ int fd; \ + void* cf_eventstream; \ + uv_async_t* cf_cb; \ + ngx_queue_t cf_events; \ + uv_sem_t cf_sem; \ + uv_mutex_t cf_mutex; \ #define UV_STREAM_PRIVATE_PLATFORM_FIELDS \ void* select; \ diff --git a/deps/uv/include/uv-private/uv-unix.h b/deps/uv/include/uv-private/uv-unix.h index 6cdea67245..91ffbe4c9e 100644 --- a/deps/uv/include/uv-private/uv-unix.h +++ b/deps/uv/include/uv-private/uv-unix.h @@ -137,6 +137,7 @@ typedef struct { uint64_t time; \ void* signal_ctx; \ uv_signal_t child_watcher; \ + int emfile_fd; \ UV_PLATFORM_LOOP_FIELDS \ #define UV_REQ_TYPE_PRIVATE /* empty */ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 4078bcd875..6a76383984 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -1789,6 +1789,7 @@ UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)); UV_EXTERN int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg); +UV_EXTERN unsigned long uv_thread_self(void); UV_EXTERN int uv_thread_join(uv_thread_t *tid); /* the presence of these unions force similar struct layout */ diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c index 78c1863ea4..63bedf56f4 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c @@ -31,7 +31,7 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* handle, int events); __attribute__((always_inline)) -inline static int uv__async_make_pending(volatile sig_atomic_t* ptr) { +static int uv__async_make_pending(volatile sig_atomic_t* ptr) { /* Do a cheap read first. */ if (*ptr) return 1; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 85d5f16449..3ad63f1f8e 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -463,7 +463,7 @@ int uv__accept(int sockfd) { while (1) { #if __linux__ - static __read_mostly int no_accept4; + static int no_accept4; if (no_accept4) goto skip; diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index b1586a3d6a..675a4f6d9c 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -43,13 +43,137 @@ static char *process_title; +/* Forward declarations */ +void uv__cf_loop_runner(void* arg); +void uv__cf_loop_cb(void* arg); + +typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t; +struct uv__cf_loop_signal_s { + void* arg; + cf_loop_signal_cb cb; + ngx_queue_t member; +}; + int uv__platform_loop_init(uv_loop_t* loop, int default_loop) { + CFRunLoopSourceContext ctx; + int r; + + loop->cf_loop = NULL; + if ((r = uv_mutex_init(&loop->cf_mutex))) + return r; + if ((r = uv_sem_init(&loop->cf_sem, 0))) + return r; + ngx_queue_init(&loop->cf_signals); + + memset(&ctx, 0, sizeof(ctx)); + ctx.info = loop; + ctx.perform = uv__cf_loop_cb; + loop->cf_cb = CFRunLoopSourceCreate(NULL, 0, &ctx); + + if ((r = uv_thread_create(&loop->cf_thread, uv__cf_loop_runner, loop))) + return r; + + /* Synchronize threads */ + uv_sem_wait(&loop->cf_sem); + assert(((volatile CFRunLoopRef) loop->cf_loop) != NULL); + return 0; } void uv__platform_loop_delete(uv_loop_t* loop) { + ngx_queue_t* item; + uv__cf_loop_signal_t* s; + + assert(loop->cf_loop != NULL); + CFRunLoopStop(loop->cf_loop); + uv_thread_join(&loop->cf_thread); + loop->cf_loop = NULL; + + uv_sem_destroy(&loop->cf_sem); + uv_mutex_destroy(&loop->cf_mutex); + + /* Free any remaining data */ + while (!ngx_queue_empty(&loop->cf_signals)) { + item = ngx_queue_head(&loop->cf_signals); + + s = ngx_queue_data(item, uv__cf_loop_signal_t, member); + + ngx_queue_remove(item); + free(s); + } +} + + +void uv__cf_loop_runner(void* arg) { + uv_loop_t* loop; + + loop = arg; + + /* Get thread's loop */ + *((volatile CFRunLoopRef*)&loop->cf_loop) = CFRunLoopGetCurrent(); + + CFRunLoopAddSource(loop->cf_loop, + loop->cf_cb, + kCFRunLoopDefaultMode); + + uv_sem_post(&loop->cf_sem); + + CFRunLoopRun(); + + CFRunLoopRemoveSource(loop->cf_loop, + loop->cf_cb, + kCFRunLoopDefaultMode); +} + + +void uv__cf_loop_cb(void* arg) { + uv_loop_t* loop; + ngx_queue_t* item; + ngx_queue_t split_head; + uv__cf_loop_signal_t* s; + + loop = arg; + + uv_mutex_lock(&loop->cf_mutex); + ngx_queue_init(&split_head); + if (!ngx_queue_empty(&loop->cf_signals)) { + ngx_queue_t* split_pos = ngx_queue_next(&loop->cf_signals); + ngx_queue_split(&loop->cf_signals, split_pos, &split_head); + } + uv_mutex_unlock(&loop->cf_mutex); + + while (!ngx_queue_empty(&split_head)) { + item = ngx_queue_head(&split_head); + + s = ngx_queue_data(item, uv__cf_loop_signal_t, member); + s->cb(s->arg); + + ngx_queue_remove(item); + free(s); + } +} + + +void uv__cf_loop_signal(uv_loop_t* loop, cf_loop_signal_cb cb, void* arg) { + uv__cf_loop_signal_t* item; + + item = malloc(sizeof(*item)); + /* XXX: Fail */ + if (item == NULL) + abort(); + + item->arg = arg; + item->cb = cb; + + uv_mutex_lock(&loop->cf_mutex); + ngx_queue_insert_tail(&loop->cf_signals, &item->member); + uv_mutex_unlock(&loop->cf_mutex); + + assert(loop->cf_loop != NULL); + CFRunLoopSourceSignal(loop->cf_cb); + CFRunLoopWakeUp(loop->cf_loop); } diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c new file mode 100644 index 0000000000..1a7e06e46c --- /dev/null +++ b/deps/uv/src/unix/fsevents.c @@ -0,0 +1,225 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + +typedef struct uv__fsevents_event_s uv__fsevents_event_t; + +struct uv__fsevents_event_s { + int events; + ngx_queue_t member; + char path[1]; +}; + + +#define UV__FSEVENTS_WALK(handle, block) \ + { \ + ngx_queue_t* curr; \ + ngx_queue_t split_head; \ + uv__fsevents_event_t* event; \ + uv_mutex_lock(&(handle)->cf_mutex); \ + ngx_queue_init(&split_head); \ + if (!ngx_queue_empty(&(handle)->cf_events)) { \ + ngx_queue_t* split_pos = ngx_queue_next(&(handle)->cf_events); \ + ngx_queue_split(&(handle)->cf_events, split_pos, &split_head); \ + } \ + uv_mutex_unlock(&(handle)->cf_mutex); \ + while (!ngx_queue_empty(&split_head)) { \ + curr = ngx_queue_head(&split_head); \ + /* Invoke callback */ \ + event = ngx_queue_data(curr, uv__fsevents_event_t, member); \ + ngx_queue_remove(curr); \ + /* Invoke block code, but only if handle wasn't closed */ \ + if (((handle)->flags & (UV_CLOSING | UV_CLOSED)) == 0) \ + block \ + /* Free allocated data */ \ + free(event); \ + } \ + } + + +void uv__fsevents_cb(uv_async_t* cb, int status) { + uv_fs_event_t* handle; + + handle = cb->data; + + UV__FSEVENTS_WALK(handle, { + if (handle->fd != -1) + handle->cb(handle, event->path, event->events, 0); + }) + + if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 && handle->fd == -1) + uv__fsevents_close(handle); +} + + +void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, + void* info, + size_t numEvents, + void* eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) { + size_t i; + int len; + char** paths; + uv_fs_event_t* handle; + uv__fsevents_event_t* event; + ngx_queue_t add_list; + + handle = info; + paths = eventPaths; + ngx_queue_init(&add_list); + + for (i = 0; i < numEvents; i++) { + /* Ignore system events */ + if (eventFlags[i] & (kFSEventStreamEventFlagUserDropped | + kFSEventStreamEventFlagKernelDropped | + kFSEventStreamEventFlagEventIdsWrapped | + kFSEventStreamEventFlagHistoryDone | + kFSEventStreamEventFlagMount | + kFSEventStreamEventFlagUnmount)) { + continue; + } + + /* TODO: Report errors */ + len = strlen(paths[i]); + event = malloc(sizeof(*event) + len); + if (event == NULL) + break; + + memcpy(event->path, paths[i], len + 1); + + if (eventFlags[i] & kFSEventStreamEventFlagItemModified) + event->events = UV_CHANGE; + else + event->events = UV_RENAME; + + ngx_queue_insert_tail(&add_list, &event->member); + } + uv_mutex_lock(&handle->cf_mutex); + ngx_queue_add(&handle->cf_events, &add_list); + uv_mutex_unlock(&handle->cf_mutex); + + uv_async_send(handle->cf_cb); +} + + +void uv__fsevents_schedule(void* arg) { + uv_fs_event_t* handle; + + handle = arg; + FSEventStreamScheduleWithRunLoop(handle->cf_eventstream, + handle->loop->cf_loop, + kCFRunLoopDefaultMode); + FSEventStreamStart(handle->cf_eventstream); + uv_sem_post(&handle->cf_sem); +} + + +int uv__fsevents_init(uv_fs_event_t* handle) { + FSEventStreamContext ctx; + FSEventStreamRef ref; + CFStringRef path; + CFArrayRef paths; + CFAbsoluteTime latency; + FSEventStreamCreateFlags flags; + + /* Initialize context */ + ctx.version = 0; + ctx.info = handle; + ctx.retain = NULL; + ctx.release = NULL; + ctx.copyDescription = NULL; + + /* Initialize paths array */ + path = CFStringCreateWithCString(NULL, + handle->filename, + CFStringGetSystemEncoding()); + paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL); + + latency = 0.15; + + /* Set appropriate flags */ + flags = kFSEventStreamCreateFlagFileEvents; + + ref = FSEventStreamCreate(NULL, + &uv__fsevents_event_cb, + &ctx, + paths, + kFSEventStreamEventIdSinceNow, + latency, + flags); + handle->cf_eventstream = ref; + + /* + * Events will occur in other thread. + * Initialize callback for getting them back into event loop's thread + */ + handle->cf_cb = malloc(sizeof(*handle->cf_cb)); + if (handle->cf_cb == NULL) + return uv__set_sys_error(handle->loop, ENOMEM); + + handle->cf_cb->data = handle; + uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb); + handle->cf_cb->flags |= UV__HANDLE_INTERNAL; + uv_unref((uv_handle_t*) handle->cf_cb); + + uv_mutex_init(&handle->cf_mutex); + uv_sem_init(&handle->cf_sem, 0); + ngx_queue_init(&handle->cf_events); + + uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle); + + return 0; +} + + +int uv__fsevents_close(uv_fs_event_t* handle) { + if (handle->cf_eventstream == NULL) + return -1; + + /* Ensure that event stream was scheduled */ + uv_sem_wait(&handle->cf_sem); + + /* Stop emitting events */ + FSEventStreamStop(handle->cf_eventstream); + + /* Release stream */ + FSEventStreamInvalidate(handle->cf_eventstream); + FSEventStreamRelease(handle->cf_eventstream); + handle->cf_eventstream = NULL; + + uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) free); + + /* Free data in queue */ + UV__FSEVENTS_WALK(handle, { + /* NOP */ + }) + + uv_mutex_destroy(&handle->cf_mutex); + uv_sem_destroy(&handle->cf_sem); + + return 0; +} diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index e2668109db..4abfedde37 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -28,12 +28,6 @@ #include #include /* abort */ -#if defined(__GNUC__) -# define __read_mostly __attribute__((__section__(".data.read_mostly"))) -#else -# define __read_mostly -#endif - #if defined(__STRICT_ANSI__) # define inline __inline #endif @@ -108,9 +102,9 @@ enum { UV_LOOP_EIO_INITIALIZED = 1 }; -inline static void uv__req_init(uv_loop_t* loop, - uv_req_t* req, - uv_req_type type) { +__attribute__((unused)) +__attribute__((always_inline)) +static void uv__req_init(uv_loop_t* loop, uv_req_t* req, uv_req_type type) { req->type = type; uv__req_register(loop, req); } @@ -195,4 +189,32 @@ void uv__udp_finish_close(uv_udp_t* handle); int uv__make_socketpair(int fds[2], int flags); int uv__make_pipe(int fds[2], int flags); +#if defined(__APPLE__) +typedef void (*cf_loop_signal_cb)(void*); + +void uv__cf_loop_signal(uv_loop_t* loop, cf_loop_signal_cb cb, void* arg); + +int uv__fsevents_init(uv_fs_event_t* handle); +int uv__fsevents_close(uv_fs_event_t* handle); + +/* OSX < 10.7 has no file events, polyfill them */ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 + +static const int kFSEventStreamCreateFlagFileEvents = 0x00000010; +static const int kFSEventStreamEventFlagItemCreated = 0x00000100; +static const int kFSEventStreamEventFlagItemRemoved = 0x00000200; +static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400; +static const int kFSEventStreamEventFlagItemRenamed = 0x00000800; +static const int kFSEventStreamEventFlagItemModified = 0x00001000; +static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000; +static const int kFSEventStreamEventFlagItemChangeOwner = 0x00004000; +static const int kFSEventStreamEventFlagItemXattrMod = 0x00008000; +static const int kFSEventStreamEventFlagItemIsFile = 0x00010000; +static const int kFSEventStreamEventFlagItemIsDir = 0x00020000; +static const int kFSEventStreamEventFlagItemIsSymlink = 0x00040000; + +#endif /* __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 */ + +#endif /* defined(__APPLE__) */ + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index be68b58677..b79dce3c28 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -89,6 +89,9 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_cb cb, int flags) { int fd; +#if defined(__APPLE__) + struct stat statbuf; +#endif /* defined(__APPLE__) */ /* We don't support any flags yet. */ assert(!flags); @@ -105,6 +108,22 @@ int uv_fs_event_init(uv_loop_t* loop, handle->fflags = 0; handle->cb = cb; handle->fd = fd; + +#if defined(__APPLE__) + /* Nullify field to perform checks later */ + handle->cf_eventstream = NULL; + + if (fstat(fd, &statbuf)) + goto fallback; + /* FSEvents works only with directories */ + if (!(statbuf.st_mode & S_IFDIR)) + goto fallback; + + return uv__fsevents_init(handle); + +fallback: +#endif /* defined(__APPLE__) */ + uv__fs_event_start(handle); return 0; @@ -112,7 +131,13 @@ 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__fs_event_stop(handle); +#else uv__fs_event_stop(handle); +#endif /* defined(__APPLE__) */ + uv__handle_stop(handle); free(handle->filename); close(handle->fd); diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index 70cdc824c7..5cd3bd0c81 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -51,6 +51,7 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) { loop->time = uv_hrtime() / 1000000; loop->async_pipefd[0] = -1; loop->async_pipefd[1] = -1; + loop->emfile_fd = -1; loop->ev = (default_loop ? ev_default_loop : ev_loop_new)(flags); ev_set_userdata(loop->ev, loop); eio_channel_init(&loop->uv_eio_channel, loop); @@ -73,4 +74,9 @@ void uv__loop_delete(uv_loop_t* loop) { uv__platform_loop_delete(loop); uv__signal_unregister(loop); ev_loop_destroy(loop->ev); + + if (loop->emfile_fd != -1) { + close(loop->emfile_fd); + loop->emfile_fd = -1; + } } diff --git a/deps/uv/src/unix/netbsd.c b/deps/uv/src/unix/netbsd.c index b1c437bf44..dab1b696be 100644 --- a/deps/uv/src/unix/netbsd.c +++ b/deps/uv/src/unix/netbsd.c @@ -19,11 +19,21 @@ */ #include "uv.h" +#include "internal.h" #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include #include #include #include @@ -34,6 +44,8 @@ #undef NANOSEC #define NANOSEC ((uint64_t) 1e9) +static char *process_title; + int uv__platform_loop_init(uv_loop_t* loop, int default_loop) { return 0; @@ -50,18 +62,20 @@ uint64_t uv_hrtime(void) { return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); } + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_LOADAVG}; - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return; + if (sysctl(which, 2, &info, &size, NULL, 0) == -1) return; avg[0] = (double) info.ldavg[0] / info.fscale; avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } + int uv_exepath(char* buffer, size_t* size) { int mib[4]; size_t cb; @@ -78,7 +92,7 @@ int uv_exepath(char* buffer, size_t* size) { mib[3] = KERN_PROC_ARGV; cb = *size; - if (sysctl(mib, 4, buffer, &cb, NULL, 0) < 0) { + if (sysctl(mib, 4, buffer, &cb, NULL, 0) == -1) { *size = 0; return -1; } @@ -87,18 +101,20 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + uint64_t uv_get_free_memory(void) { struct uvmexp info; size_t size = sizeof(info); int which[] = {CTL_VM, VM_UVMEXP}; - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + if (sysctl(which, 2, &info, &size, NULL, 0) == -1) { return -1; } return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } + uint64_t uv_get_total_memory(void) { #if defined(HW_PHYSMEM64) uint64_t info; @@ -109,9 +125,229 @@ uint64_t uv_get_total_memory(void) { #endif size_t size = sizeof(info); - if (sysctl(which, 2, &info, &size, NULL, 0) < 0) { + if (sysctl(which, 2, &info, &size, NULL, 0) == -1) { return -1; } return (uint64_t) info; } + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + if (process_title) free(process_title); + + process_title = strdup(title); + setproctitle("%s", title); + + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title) { + strncpy(buffer, process_title, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + kvm_t *kd = NULL; + struct kinfo_proc2 *kinfo = NULL; + pid_t pid; + int nprocs; + int max_size = sizeof(struct kinfo_proc2); + int page_size; + + page_size = getpagesize(); + pid = getpid(); + + kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open"); + + if (kd == NULL) goto error; + + kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs); + if (kinfo == NULL) goto error; + + *rss = kinfo->p_vm_rssize * page_size; + + kvm_close(kd); + + return uv_ok_; + +error: + if (kd) kvm_close(kd); + return uv__new_sys_error(errno); +} + + +uv_err_t uv_uptime(double* uptime) { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, 2, &info, &size, NULL, 0) == -1) { + return uv__new_sys_error(errno); + } + + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK); + unsigned int multiplier = ((uint64_t)1000L / ticks); + unsigned int cur = 0; + uv_cpu_info_t* cpu_info; + u_int64_t* cp_times; + char model[512]; + u_int64_t cpuspeed; + int numcpus; + size_t size; + int i; + + size = sizeof(model); + if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) == -1 && + sysctlbyname("hw.model", &model, &size, NULL, 0) == -1) { + return uv__new_sys_error(errno); + } + + size = sizeof(numcpus); + if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0) == -1) { + return uv__new_sys_error(errno); + } + *count = numcpus; + + /* Only i386 and amd64 have machdep.tsc_freq */ + size = sizeof(cpuspeed); + if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0) == -1) { + cpuspeed = 0; + } + + size = numcpus * CPUSTATES * sizeof(*cp_times); + cp_times = malloc(size); + if (cp_times == NULL) { + return uv__new_artificial_error(UV_ENOMEM); + } + if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0) == -1) { + return uv__new_sys_error(errno); + } + + *cpu_infos = malloc(numcpus * sizeof(**cpu_infos)); + if (!(*cpu_infos)) { + free(cp_times); + free(*cpu_infos); + return uv__new_artificial_error(UV_ENOMEM); + } + + for (i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier; + cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier; + cpu_info->model = strdup(model); + cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6); + cur += CPUSTATES; + } + free(cp_times); + return uv_ok_; +} + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + struct ifaddrs *addrs; + struct ifaddrs *ent; + uv_interface_address_t* address; + + if (getifaddrs(&addrs) != 0) { + return uv__new_sys_error(errno); + } + + *count = 0; + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) || + (ent->ifa_addr == NULL) || + (ent->ifa_addr->sa_family != PF_INET)) { + continue; + } + (*count)++; + } + + *addresses = malloc(*count * sizeof(**addresses)); + + if (!(*addresses)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) { + continue; + } + + if (ent->ifa_addr == NULL) { + continue; + } + + if (ent->ifa_addr->sa_family != PF_INET) { + continue; + } + + address->name = strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); + } + + address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK) ? 1 : 0; + + address++; + } + + freeifaddrs(addrs); + + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 829f79aafd..7c552a7361 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -117,7 +117,7 @@ static void uv__chld(uv_signal_t* handle, int signum) { int uv__make_socketpair(int fds[2], int flags) { #if __linux__ - static __read_mostly int no_cloexec; + static int no_cloexec; if (no_cloexec) goto skip; @@ -153,7 +153,7 @@ skip: int uv__make_pipe(int fds[2], int flags) { #if __linux__ - static __read_mostly int no_pipe2; + static int no_pipe2; if (no_pipe2) goto skip; diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 344118569b..5321d90f4e 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -62,6 +62,29 @@ static void uv__read(uv_stream_t* stream); static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, int events); +/* Used by the accept() EMFILE party trick. */ +static int uv__open_cloexec(const char* path, int flags) { + int fd; + +#if defined(__linux__) + fd = open(path, flags | UV__O_CLOEXEC); + if (fd != -1) + return fd; + + if (errno != EINVAL) + return -1; + + /* O_CLOEXEC not supported. */ +#endif + + fd = open(path, flags); + if (fd != -1) + uv__cloexec(fd, 1); + + return fd; +} + + static size_t uv__buf_count(uv_buf_t bufs[], int bufcnt) { size_t total = 0; int i; @@ -90,6 +113,9 @@ void uv__stream_init(uv_loop_t* loop, ngx_queue_init(&stream->write_completed_queue); stream->write_queue_size = 0; + if (loop->emfile_fd == -1) + loop->emfile_fd = uv__open_cloexec("/", O_RDONLY); + #if defined(__APPLE__) stream->select = NULL; #endif /* defined(__APPLE_) */ @@ -370,10 +396,56 @@ static void uv__next_accept(uv_idle_t* idle, int status) { } +/* Implements a best effort approach to mitigating accept() EMFILE errors. + * We have a spare file descriptor stashed away that we close to get below + * the EMFILE limit. Next, we accept all pending connections and close them + * immediately to signal the clients that we're overloaded - and we are, but + * we still keep on trucking. + * + * There is one caveat: it's not reliable in a multi-threaded environment. + * The file descriptor limit is per process. Our party trick fails if another + * thread opens a file or creates a socket in the time window between us + * calling close() and accept(). + */ +static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { + int fd; + int r; + + if (loop->emfile_fd == -1) + return -1; + + close(loop->emfile_fd); + + for (;;) { + fd = uv__accept(accept_fd); + + if (fd != -1) { + close(fd); + continue; + } + + if (errno == EINTR) + continue; + + if (errno == EAGAIN || errno == EWOULDBLOCK) + r = 0; + else + r = -1; + + loop->emfile_fd = uv__open_cloexec("/", O_RDONLY); + + return r; + } +} + + void uv__server_io(uv_loop_t* loop, uv__io_t* w, int events) { + static int use_emfile_trick = -1; + uv_stream_t* stream; int fd; - uv_stream_t* stream = container_of(w, uv_stream_t, read_watcher); + int r; + stream = container_of(w, uv_stream_t, read_watcher); assert(events == UV__IO_READ); assert(!(stream->flags & UV_CLOSING)); @@ -389,31 +461,48 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, int events) { assert(stream->accepted_fd < 0); fd = uv__accept(stream->fd); - if (fd < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - /* No problem. */ - return; - } else if (errno == EMFILE) { - /* TODO special trick. unlock reserved socket, accept, close. */ - return; - } else if (errno == ECONNABORTED) { - /* ignore */ + if (fd == -1) { + switch (errno) { +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EAGAIN: + return; /* Not an error. */ + + case ECONNABORTED: + continue; /* Ignore. */ + + 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, stream->fd)); + if (r == 0) + continue; + } + + /* Fall through. */ + + default: + uv__set_sys_error(loop, errno); + stream->connection_cb(stream, -1); continue; - } else { - uv__set_sys_error(stream->loop, errno); - stream->connection_cb((uv_stream_t*)stream, -1); - } - } else { - stream->accepted_fd = fd; - stream->connection_cb(stream, 0); - - if (stream->accepted_fd != -1 || - (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) { - /* The user hasn't yet accepted called uv_accept() */ - uv__io_stop(stream->loop, &stream->read_watcher); - break; } } + + stream->accepted_fd = fd; + stream->connection_cb(stream, 0); + + if (stream->accepted_fd != -1 || + (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) { + /* The user hasn't yet accepted called uv_accept() */ + uv__io_stop(loop, &stream->read_watcher); + break; + } } if (stream->fd != -1 && diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index 441473cdac..186150be57 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -247,6 +247,7 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { if (uv_idle_init(tcp->loop, tcp->idle_handle)) abort(); + tcp->idle_handle->flags |= UV__HANDLE_INTERNAL; tcp->flags |= UV_TCP_SINGLE_ACCEPT; @@ -331,7 +332,10 @@ int uv__tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { } #endif -#ifdef TCP_KEEPALIVE + /* Solaris/SmartOS, if you don't support keep-alive, + * then don't advertise it in your system headers... + */ +#if defined(TCP_KEEPALIVE) && !defined(__sun) if (enable && setsockopt(handle->fd, IPPROTO_TCP, TCP_KEEPALIVE, diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 75dfcd89c2..dd91dbb28d 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -314,6 +314,15 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { } +unsigned long uv_thread_self(void) { +#ifdef _WIN32 + return (unsigned long) GetCurrentThreadId(); +#else + return (unsigned long) pthread_self(); +#endif +} + + void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { ngx_queue_t* q; uv_handle_t* h; diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c index 9a83ea8971..a9e0e7fbdf 100644 --- a/deps/uv/src/win/error.c +++ b/deps/uv/src/win/error.c @@ -144,6 +144,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case ERROR_BAD_PIPE: return UV_EPIPE; case ERROR_NO_DATA: return UV_EPIPE; case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE; + case WSAESHUTDOWN: return UV_EPIPE; case ERROR_PIPE_BUSY: return UV_EBUSY; case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT; case WSAETIMEDOUT: return UV_ETIMEDOUT; diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index b0d1d18ed0..fe38890627 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -52,9 +52,8 @@ #define UV_HANDLE_LISTENING 0x00000800 #define UV_HANDLE_CONNECTION 0x00001000 #define UV_HANDLE_CONNECTED 0x00002000 -#define UV_HANDLE_EOF 0x00004000 -#define UV_HANDLE_SHUTTING 0x00008000 -#define UV_HANDLE_SHUT 0x00010000 +#define UV_HANDLE_READABLE 0x00008000 +#define UV_HANDLE_WRITABLE 0x00010000 #define UV_HANDLE_READ_PENDING 0x00020000 #define UV_HANDLE_SYNC_BYPASS_IOCP 0x00040000 #define UV_HANDLE_ZERO_READ 0x00080000 diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index 62cdef544d..819091efe6 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -114,7 +114,7 @@ static HANDLE open_named_pipe(WCHAR* name, DWORD* duplex_flags) { FILE_FLAG_OVERLAPPED, NULL); if (pipeHandle != INVALID_HANDLE_VALUE) { - *duplex_flags = 0; + *duplex_flags = UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; return pipeHandle; } @@ -133,7 +133,7 @@ static HANDLE open_named_pipe(WCHAR* name, DWORD* duplex_flags) { NULL); if (pipeHandle != INVALID_HANDLE_VALUE) { - *duplex_flags = UV_HANDLE_SHUTTING; + *duplex_flags = UV_HANDLE_READABLE; return pipeHandle; } } @@ -148,7 +148,7 @@ static HANDLE open_named_pipe(WCHAR* name, DWORD* duplex_flags) { NULL); if (pipeHandle != INVALID_HANDLE_VALUE) { - *duplex_flags = UV_HANDLE_EOF; + *duplex_flags = UV_HANDLE_WRITABLE; return pipeHandle; } } @@ -316,7 +316,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { /* Failure */ UNREGISTER_HANDLE_REQ(loop, handle, req); - handle->flags &= ~UV_HANDLE_SHUTTING; + handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */ if (req->cb) { uv__set_sys_error(loop, pRtlNtStatusToDosError(nt_status)); req->cb(req, -1); @@ -343,7 +343,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { /* Failure. */ UNREGISTER_HANDLE_REQ(loop, handle, req); - handle->flags &= ~UV_HANDLE_SHUTTING; + handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */ if (req->cb) { uv__set_sys_error(loop, GetLastError()); req->cb(req, -1); @@ -630,7 +630,7 @@ void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { } if (handle->flags & UV_HANDLE_CONNECTION) { - handle->flags |= UV_HANDLE_SHUTTING; + handle->flags &= ~UV_HANDLE_WRITABLE; eof_timer_destroy(handle); } @@ -659,6 +659,7 @@ void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle) { uv_want_endgame(loop, (uv_handle_t*) handle); } + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); uv__handle_closing(handle); } @@ -746,6 +747,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { /* Initialize the client handle and copy the pipeHandle to the client */ uv_pipe_connection_init(pipe_client); pipe_client->handle = req->pipeHandle; + pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; /* Prepare the req to pick up a new connection */ server->pending_accepts = req->next_pending; @@ -964,16 +966,6 @@ static int uv_pipe_read_start_impl(uv_pipe_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb, uv_read2_cb read2_cb) { uv_loop_t* loop = handle->loop; - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - uv__set_artificial_error(loop, UV_EINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_EOF) { - uv__set_artificial_error(loop, UV_EOF); - return -1; - } - handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); handle->read_cb = read_cb; @@ -1072,16 +1064,6 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req, assert(handle->handle != INVALID_HANDLE_VALUE); - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - uv__set_artificial_error(loop, UV_EINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_SHUTTING) { - uv__set_artificial_error(loop, UV_EOF); - return -1; - } - uv_req_init(loop, (uv_req_t*) req); req->type = UV_WRITE; req->handle = (uv_stream_t*) handle; @@ -1253,7 +1235,7 @@ static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, /* so discard it. */ eof_timer_destroy(handle); - handle->flags |= UV_HANDLE_EOF; + handle->flags &= ~UV_HANDLE_READABLE; uv_read_stop((uv_stream_t*) handle); uv__set_artificial_error(loop, UV_EOF); @@ -1483,8 +1465,8 @@ void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, uv_queue_non_overlapped_write(handle); } - if (handle->write_reqs_pending == 0 && - handle->flags & UV_HANDLE_SHUTTING) { + if (handle->shutdown_req != NULL && + handle->write_reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*)handle); } @@ -1548,7 +1530,7 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, /* Initialize and optionally start the eof timer. */ /* This makes no sense if we've already seen EOF. */ - if (!(handle->flags & UV_HANDLE_EOF)) { + if (handle->flags & UV_HANDLE_READABLE) { eof_timer_init(handle); /* If reading start the timer right now. */ @@ -1662,6 +1644,7 @@ void uv_pipe_open(uv_pipe_t* pipe, uv_file file) { uv_pipe_connection_init(pipe); pipe->handle = os_handle; + pipe->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; if (pipe->ipc) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); diff --git a/deps/uv/src/win/process-stdio.c b/deps/uv/src/win/process-stdio.c index 7777740477..89e2cd990b 100644 --- a/deps/uv/src/win/process-stdio.c +++ b/deps/uv/src/win/process-stdio.c @@ -163,6 +163,9 @@ 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; + *child_pipe_ptr = child_pipe; return uv_ok_; diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c index 195e0b8018..097f349794 100644 --- a/deps/uv/src/win/stream.c +++ b/deps/uv/src/win/stream.c @@ -55,6 +55,16 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { + if (handle->flags & UV_HANDLE_READING) { + uv__set_sys_error(handle->loop, UV_EALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_READABLE)) { + uv__set_artificial_error(handle->loop, UV_ENOTCONN); + return -1; + } + switch (handle->type) { case UV_TCP: return uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); @@ -71,6 +81,16 @@ int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, int uv_read2_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read2_cb read_cb) { + if (handle->flags & UV_HANDLE_READING) { + uv__set_sys_error(handle->loop, UV_EALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_READABLE)) { + uv__set_artificial_error(handle->loop, UV_ENOTCONN); + return -1; + } + switch (handle->type) { case UV_NAMED_PIPE: return uv_pipe_read2_start((uv_pipe_t*)handle, alloc_cb, read_cb); @@ -82,14 +102,15 @@ int uv_read2_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, int uv_read_stop(uv_stream_t* handle) { + if (!(handle->flags & UV_HANDLE_READING)) + return 0; + if (handle->type == UV_TTY) { return uv_tty_read_stop((uv_tty_t*) handle); - } else if (handle->flags & UV_HANDLE_READING) { + } else { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(handle->loop, handle); return 0; - } else { - return 0; } } @@ -98,6 +119,11 @@ int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { uv_loop_t* loop = handle->loop; + if (!(handle->flags & UV_HANDLE_WRITABLE)) { + uv__set_artificial_error(loop, UV_EPIPE); + return -1; + } + switch (handle->type) { case UV_TCP: return uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, bufcnt, cb); @@ -117,6 +143,11 @@ int uv_write2(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, uv_stream_t* send_handle, uv_write_cb cb) { uv_loop_t* loop = handle->loop; + if (!(handle->flags & UV_HANDLE_WRITABLE)) { + uv__set_artificial_error(loop, UV_EPIPE); + return -1; + } + switch (handle->type) { case UV_NAMED_PIPE: return uv_pipe_write2(loop, req, (uv_pipe_t*) handle, bufs, bufcnt, send_handle, cb); @@ -131,13 +162,13 @@ int uv_write2(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { uv_loop_t* loop = handle->loop; - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - uv__set_sys_error(loop, WSAEINVAL); + if (!(handle->flags & UV_HANDLE_WRITABLE)) { + uv__set_artificial_error(loop, UV_EPIPE); return -1; } - if (handle->flags & UV_HANDLE_SHUTTING) { - uv__set_sys_error(loop, WSAESHUTDOWN); + if (!(handle->flags & UV_HANDLE_WRITABLE)) { + uv__set_artificial_error(loop, UV_EPIPE); return -1; } @@ -146,7 +177,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { req->handle = handle; req->cb = cb; - handle->flags |= UV_HANDLE_SHUTTING; + handle->flags &= ~UV_HANDLE_WRITABLE; handle->shutdown_req = req; handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); @@ -158,10 +189,10 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { int uv_is_readable(const uv_stream_t* handle) { - return !(handle->flags & UV_HANDLE_EOF); + return !!(handle->flags & UV_HANDLE_READABLE); } int uv_is_writable(const uv_stream_t* handle) { - return !(handle->flags & UV_HANDLE_SHUTTING); + return !!(handle->flags & UV_HANDLE_WRITABLE); } diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 111f03c4ab..0bc01c613d 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -169,7 +169,6 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { uv__set_artificial_error(loop, UV_ECANCELED); } else if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { status = 0; - handle->flags |= UV_HANDLE_SHUT; } else { status = -1; uv__set_sys_error(loop, WSAGetLastError()); @@ -605,7 +604,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { } else { uv_connection_init((uv_stream_t*) client); /* AcceptEx() implicitly binds the accepted socket. */ - client->flags |= UV_HANDLE_BOUND; + client->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } /* Prepare the req to pick up a new connection */ @@ -646,21 +645,6 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - uv__set_sys_error(loop, WSAEINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_READING) { - uv__set_sys_error(loop, WSAEALREADY); - return -1; - } - - if (handle->flags & UV_HANDLE_EOF) { - uv__set_sys_error(loop, WSAESHUTDOWN); - return -1; - } - handle->flags |= UV_HANDLE_READING; handle->read_cb = read_cb; handle->alloc_cb = alloc_cb; @@ -855,16 +839,6 @@ int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, int result; DWORD bytes; - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - uv__set_sys_error(loop, WSAEINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_SHUTTING) { - uv__set_sys_error(loop, WSAESHUTDOWN); - return -1; - } - uv_req_init(loop, (uv_req_t*) req); req->type = UV_WRITE; req->handle = (uv_stream_t*) handle; @@ -970,7 +944,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); } - handle->flags |= UV_HANDLE_EOF; + handle->flags &= ~UV_HANDLE_READABLE; uv__set_error(loop, UV_EOF, ERROR_SUCCESS); buf.base = 0; @@ -1001,9 +975,8 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, } } else { /* Connection closed */ - handle->flags &= ~UV_HANDLE_READING; + handle->flags &= ~(UV_HANDLE_READING | UV_HANDLE_READABLE); DECREASE_ACTIVE_COUNT(loop, handle); - handle->flags |= UV_HANDLE_EOF; uv__set_error(loop, UV_EOF, ERROR_SUCCESS); handle->read_cb((uv_stream_t*)handle, -1, buf); @@ -1070,7 +1043,7 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, } handle->write_reqs_pending--; - if (handle->flags & UV_HANDLE_SHUTTING && + if (handle->shutdown_req != NULL && handle->write_reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*)handle); } @@ -1139,6 +1112,7 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, NULL, 0) == 0) { uv_connection_init((uv_stream_t*)handle); + handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; loop->active_tcp_streams++; ((uv_connect_cb)req->cb)(req, 0); } else { @@ -1181,6 +1155,7 @@ int uv_tcp_import(uv_tcp_t* tcp, WSAPROTOCOL_INFOW* socket_protocol_info, if (tcp_connection) { uv_connection_init((uv_stream_t*)tcp); + tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } tcp->flags |= UV_HANDLE_BOUND; @@ -1342,7 +1317,6 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) { /* Just do shutdown on non-shared sockets, which ensures graceful close. */ shutdown(tcp->socket, SD_SEND); - tcp->flags |= UV_HANDLE_SHUT; } else if (uv_tcp_try_cancel_io(tcp) == 0) { /* In case of a shared socket, we try to cancel all outstanding I/O, */ @@ -1397,6 +1371,7 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { tcp->flags |= UV_HANDLE_TCP_SOCKET_CLOSED; } + tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); uv__handle_closing(tcp); if (tcp->reqs_pending == 0) { diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index 19edf7d29a..7a84506fde 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -140,7 +140,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { if (readable) { /* Initialize TTY input specific fields. */ tty->original_console_mode = original_console_mode; - tty->flags |= UV_HANDLE_TTY_READABLE; + tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE; tty->read_line_handle = NULL; tty->read_line_buffer = uv_null_buf_; tty->read_raw_wait = NULL; @@ -152,6 +152,8 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { memset(&tty->last_input_record, 0, sizeof tty->last_input_record); } else { /* TTY output specific fields. */ + tty->flags |= UV_HANDLE_READABLE; + /* Init utf8-to-utf16 conversion state. */ tty->utf8_bytes_left = 0; tty->utf8_codepoint = 0; @@ -826,15 +828,9 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, int uv_tty_read_stop(uv_tty_t* handle) { uv_loop_t* loop = handle->loop; - if (!(handle->flags & UV_HANDLE_TTY_READABLE)) { - uv__set_artificial_error(handle->loop, UV_EINVAL); - return -1; - } - if (handle->flags & UV_HANDLE_READING) { - handle->flags &= ~UV_HANDLE_READING; - DECREASE_ACTIVE_COUNT(loop, handle); - } + handle->flags &= ~UV_HANDLE_READING; + DECREASE_ACTIVE_COUNT(loop, handle); /* Cancel raw read */ if ((handle->flags & UV_HANDLE_READ_PENDING) && @@ -1750,17 +1746,6 @@ int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { DWORD error; - if (handle->flags & UV_HANDLE_TTY_READABLE) { - uv__set_artificial_error(handle->loop, UV_EINVAL); - return -1; - } - - if ((handle->flags & UV_HANDLE_SHUTTING) || - (handle->flags & UV_HANDLE_CLOSING)) { - uv__set_sys_error(loop, WSAESHUTDOWN); - return -1; - } - uv_req_init(loop, (uv_req_t*) req); req->type = UV_WRITE; req->handle = (uv_stream_t*) handle; @@ -1796,7 +1781,7 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, } handle->write_reqs_pending--; - if (handle->flags & UV_HANDLE_SHUTTING && + if (handle->shutdown_req != NULL && handle->write_reqs_pending == 0) { uv_want_endgame(loop, (uv_handle_t*)handle); } @@ -1808,14 +1793,10 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, void uv_tty_close(uv_tty_t* handle) { CloseHandle(handle->handle); - if (handle->flags & UV_HANDLE_TTY_READABLE) { - /* Readable TTY handle */ + if (handle->flags & UV_HANDLE_READING) uv_tty_read_stop(handle); - } else { - /* Writable TTY handle */ - handle->flags |= UV_HANDLE_SHUTTING; - } + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); uv__handle_closing(handle); if (handle->reqs_pending == 0) { diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index 4d4cfbc31b..257d64ca54 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -98,7 +98,8 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_RENAME); - ASSERT(filename == NULL || strcmp(filename, "file1") == 0); + ASSERT(filename == NULL || strcmp(filename, "file1") == 0 || + strstr(filename, "watch_dir") != NULL); uv_close((uv_handle_t*)handle, close_cb); } diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 8ce01ebfda..e44087a572 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -147,7 +147,7 @@ 'libraries': [ '-lm' ] }], [ 'OS=="mac"', { - 'sources': [ 'src/unix/darwin.c' ], + 'sources': [ 'src/unix/darwin.c', 'src/unix/fsevents.c' ], 'direct_dependent_settings': { 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/CoreServices.framework', @@ -209,6 +209,18 @@ 'EIO_CONFIG_H="config_openbsd.h"', ], }], + [ 'OS=="netbsd"', { + 'sources': [ 'src/unix/netbsd.c' ], + 'defines': [ + 'EV_CONFIG_H="config_netbsd.h"', + 'EIO_CONFIG_H="config_netbsd.h"', + ], + 'direct_dependent_settings': { + 'libraries': [ + '-lkvm', + ], + }, + }], [ 'OS=="mac" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd"', { 'sources': [ 'src/unix/kqueue.c' ], }],