From 8d37b6c92b61d9ab66b48ca67fd3e42751f5cf43 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 22 Sep 2011 19:41:43 -0700 Subject: upgrade libuv --- deps/uv/config-unix.mk | 2 +- deps/uv/include/uv-private/uv-linux.h | 29 +++ deps/uv/include/uv-private/uv-unix.h | 9 + deps/uv/include/uv-private/uv-win.h | 13 +- deps/uv/include/uv.h | 49 ++++- deps/uv/src/unix/core.c | 7 + deps/uv/src/unix/cygwin.c | 16 ++ deps/uv/src/unix/darwin.c | 17 ++ deps/uv/src/unix/freebsd.c | 19 +- deps/uv/src/unix/internal.h | 16 ++ deps/uv/src/unix/linux.c | 134 +++++++++++- deps/uv/src/unix/netbsd.c | 19 +- deps/uv/src/unix/sunos.c | 19 +- deps/uv/src/unix/tty.c | 41 ++++ deps/uv/src/win/fs-event.c | 384 ++++++++++++++++++++++++++++++++++ deps/uv/src/win/handle.c | 8 + deps/uv/src/win/internal.h | 8 + deps/uv/src/win/req.c | 4 + deps/uv/src/win/tty.c | 12 ++ deps/uv/test/test-fs-event.c | 217 +++++++++++++++++++ deps/uv/test/test-list.h | 6 + deps/uv/test/test-tty.c | 25 +++ deps/uv/uv.gyp | 3 + 23 files changed, 1050 insertions(+), 7 deletions(-) create mode 100644 deps/uv/include/uv-private/uv-linux.h create mode 100644 deps/uv/src/win/fs-event.c create mode 100644 deps/uv/test/test-fs-event.c (limited to 'deps/uv') diff --git a/deps/uv/config-unix.mk b/deps/uv/config-unix.mk index 2f8525c14..59bf8f17f 100644 --- a/deps/uv/config-unix.mk +++ b/deps/uv/config-unix.mk @@ -22,7 +22,7 @@ CC = $(PREFIX)gcc AR = $(PREFIX)ar E= CSTDFLAG=--std=c89 -pedantic -Wall -Wextra -Wno-unused-parameter -CFLAGS=-g +CFLAGS += -g CPPFLAGS += -Isrc/unix/ev LINKFLAGS=-lm diff --git a/deps/uv/include/uv-private/uv-linux.h b/deps/uv/include/uv-private/uv-linux.h new file mode 100644 index 000000000..7f7b05932 --- /dev/null +++ b/deps/uv/include/uv-private/uv-linux.h @@ -0,0 +1,29 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_LINUX_H +#define UV_LINUX_H + +#define UV_FS_EVENT_PRIVATE_FIELDS \ + ev_io read_watcher; \ + uv_fs_event_cb cb; \ + +#endif /* UV_LINUX_H */ diff --git a/deps/uv/include/uv-private/uv-unix.h b/deps/uv/include/uv-private/uv-unix.h index 6a0ef90ba..5fa132872 100644 --- a/deps/uv/include/uv-private/uv-unix.h +++ b/deps/uv/include/uv-private/uv-unix.h @@ -27,6 +27,10 @@ #include "ev.h" #include "eio.h" +#if defined(__linux__) +#include "uv-private/uv-linux.h" +#endif + #include #include #include @@ -42,6 +46,11 @@ typedef struct { typedef int uv_file; +/* Stub. Remove it once all platforms support the file watcher API. */ +#ifndef UV_FS_EVENT_PRIVATE_FIELDS +#define UV_FS_EVENT_PRIVATE_FIELDS /* empty */ +#endif + #define UV_LOOP_PRIVATE_FIELDS \ ares_channel channel; \ /* \ diff --git a/deps/uv/include/uv-private/uv-win.h b/deps/uv/include/uv-private/uv-win.h index c43313f7b..f203d6728 100644 --- a/deps/uv/include/uv-private/uv-win.h +++ b/deps/uv/include/uv-private/uv-win.h @@ -86,7 +86,8 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); UV_GETADDRINFO_REQ, \ UV_PROCESS_EXIT, \ UV_PROCESS_CLOSE, \ - UV_UDP_RECV + UV_UDP_RECV, \ + UV_FS_EVENT_REQ #define UV_REQ_PRIVATE_FIELDS \ union { \ @@ -261,6 +262,16 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); #define UV_WORK_PRIVATE_FIELDS \ +#define UV_FS_EVENT_PRIVATE_FIELDS \ + struct uv_fs_event_req_s { \ + UV_REQ_FIELDS \ + } req; \ + HANDLE dir_handle; \ + int req_pending; \ + uv_fs_event_cb cb; \ + wchar_t* filew; \ + int is_path_dir; \ + char* buffer; #define UV_TTY_PRIVATE_FIELDS /* empty */ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 474161659..c9c5c9ab3 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -65,6 +65,8 @@ typedef struct uv_write_s uv_write_t; typedef struct uv_connect_s uv_connect_t; typedef struct uv_udp_send_s uv_udp_send_t; typedef struct uv_fs_s uv_fs_t; +/* uv_fs_event_t is a subclass of uv_handle_t. */ +typedef struct uv_fs_event_s uv_fs_event_t; typedef struct uv_work_s uv_work_t; #if defined(__unix__) || defined(__POSIX__) || defined(__APPLE__) @@ -137,6 +139,15 @@ typedef void (*uv_fs_cb)(uv_fs_t* req); typedef void (*uv_work_cb)(uv_work_t* req); typedef void (*uv_after_work_cb)(uv_work_t* req); +/* +* This will be called repeatedly after the uv_fs_event_t is initialized. +* If uv_fs_event_t was initialized with a directory the filename parameter +* will be a relative path to a file contained in the directory. +* The events paramenter is an ORed mask of enum uv_fs_event elements. +*/ +typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, + int events, int status); + /* Expand this list if necessary. */ typedef enum { @@ -201,7 +212,8 @@ typedef enum { UV_ASYNC, UV_ARES_TASK, UV_ARES_EVENT, - UV_PROCESS + UV_PROCESS, + UV_FS_EVENT } uv_handle_type; typedef enum { @@ -612,6 +624,17 @@ int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd); */ int uv_tty_set_mode(uv_tty_t*, int mode); +/* + * Gets the current Window size. On success zero is returned. + */ +int uv_tty_get_winsize(uv_tty_t*, int* width, int* height); + +/* + * Used to detect what type of stream should be used with a given file + * descriptor. Usually this will be used during initialization to guess the + * type of the stdio streams. + */ +uv_handle_type uv_guess_handle(uv_file file); /* * uv_pipe_t is a subclass of uv_stream_t @@ -1002,6 +1025,27 @@ int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, int gid, uv_fs_cb cb); +enum uv_fs_event { + UV_RENAME = 1, + UV_CHANGE = 2 +}; + + +struct uv_fs_event_s { + UV_HANDLE_FIELDS + char* filename; + UV_FS_EVENT_PRIVATE_FIELDS +}; + + +/* +* If filename is a directory then we will watch for all events in that +* directory. If filename is a file - we will only get events from that +* file. Subdirectories are not watched. +*/ +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb); + /* Utility */ /* Convert string ip addresses to binary structures */ @@ -1037,6 +1081,7 @@ union uv_any_handle { uv_async_t async; uv_timer_t timer; uv_getaddrinfo_t getaddrinfo; + uv_fs_event_t fs_event; }; union uv_any_req { @@ -1064,6 +1109,7 @@ struct uv_counters_s { uint64_t async_init; uint64_t timer_init; uint64_t process_init; + uint64_t fs_event_init; }; @@ -1097,6 +1143,7 @@ struct uv_loop_s { #undef UV_GETADDRINFO_PRIVATE_FIELDS #undef UV_FS_REQ_PRIVATE_FIELDS #undef UV_WORK_PRIVATE_FIELDS +#undef UV_FS_EVENT_PRIVATE_FIELDS #ifdef __cplusplus } diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 58d129870..39d5641fc 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -137,6 +137,10 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { ev_child_stop(process->loop->ev, &process->child_watcher); break; + case UV_FS_EVENT: + uv__fs_event_destroy((uv_fs_event_t*)handle); + break; + default: assert(0); } @@ -250,6 +254,9 @@ void uv__finish_close(uv_handle_t* handle) { assert(!ev_is_active(&((uv_process_t*)handle)->child_watcher)); break; + case UV_FS_EVENT: + break; + default: assert(0); break; diff --git a/deps/uv/src/unix/cygwin.c b/deps/uv/src/unix/cygwin.c index d56a8e839..6702eb923 100644 --- a/deps/uv/src/unix/cygwin.c +++ b/deps/uv/src/unix/cygwin.c @@ -20,8 +20,10 @@ #include "uv.h" +#include #include #include +#include #include #undef NANOSEC @@ -50,3 +52,17 @@ int uv_exepath(char* buffer, size_t* size) { buffer[*size] = '\0'; return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index bfa06eede..6bc122cc6 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -20,7 +20,10 @@ #include "uv.h" +#include #include +#include + #include #include #include @@ -63,3 +66,17 @@ int uv_exepath(char* buffer, size_t* size) { *size = strlen(buffer); return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/deps/uv/src/unix/freebsd.c b/deps/uv/src/unix/freebsd.c index 6766538f0..449aad4c2 100644 --- a/deps/uv/src/unix/freebsd.c +++ b/deps/uv/src/unix/freebsd.c @@ -20,15 +20,18 @@ #include "uv.h" +#include #include -#include +#include #include #include +#include #undef NANOSEC #define NANOSEC 1000000000 + uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -63,3 +66,17 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 98fc51fc6..42283ca2a 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -25,6 +25,8 @@ #include "uv-common.h" #include "uv-eio.h" +#include /* offsetof */ + #if defined(__linux__) #include @@ -59,6 +61,17 @@ # define HAVE_FUTIMES #endif +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member))) + +#define SAVE_ERRNO(block) \ + do { \ + int _saved_errno = errno; \ + do { block; } while (0); \ + errno = _saved_errno; \ + } \ + while (0); + /* flags */ enum { UV_CLOSING = 0x00000001, /* uv_close() called but not finished. */ @@ -110,4 +123,7 @@ int uv_pipe_cleanup(uv_pipe_t* handle); void uv__udp_destroy(uv_udp_t* handle); void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w); +/* fs */ +void uv__fs_event_destroy(uv_fs_event_t* handle); + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/deps/uv/src/unix/linux.c b/deps/uv/src/unix/linux.c index 03396e9cd..0b4ce64dd 100644 --- a/deps/uv/src/unix/linux.c +++ b/deps/uv/src/unix/linux.c @@ -19,15 +19,29 @@ */ #include "uv.h" +#include "internal.h" #include -#include +#include +#include +#include +#include + +#include #include #include #undef NANOSEC #define NANOSEC 1000000000 + +/* Don't look aghast, this is exactly how glibc's basename() works. */ +static char* basename_r(const char* path) { + char* s = strrchr(path, '/'); + return s ? (s + 1) : (char*)path; +} + + /* * There's probably some way to get time from Linux than gettimeofday(). What * it is, I don't know. @@ -49,3 +63,121 @@ int uv_exepath(char* buffer, size_t* size) { buffer[*size] = '\0'; return 0; } + + +static int new_inotify_fd(void) { +#if defined(IN_NONBLOCK) && defined(IN_CLOEXEC) + return inotify_init1(IN_NONBLOCK | IN_CLOEXEC); +#else + int fd; + + if ((fd = inotify_init()) == -1) + return -1; + + if (uv__cloexec(fd, 1) || uv__nonblock(fd, 1)) { + SAVE_ERRNO(uv__close(fd)); + fd = -1; + } + + return fd; +#endif +} + + +static void uv__inotify_read(EV_P_ ev_io* w, int revents) { + struct inotify_event* e; + uv_fs_event_t* handle; + const char* filename; + ssize_t size; + int events; + char *p; + /* needs to be large enough for sizeof(inotify_event) + strlen(filename) */ + char buf[4096]; + + handle = container_of(w, uv_fs_event_t, read_watcher); + + do { + do { + size = read(handle->fd, buf, sizeof buf); + } + while (size == -1 && errno == EINTR); + + if (size == -1) { + assert(errno == EAGAIN || errno == EWOULDBLOCK); + break; + } + + assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ + + /* Now we have one or more inotify_event structs. */ + for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { + e = (void*)p; + + events = 0; + if (e->mask & (IN_ATTRIB|IN_MODIFY)) + events |= UV_CHANGE; + if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) + events |= UV_RENAME; + + /* inotify does not return the filename when monitoring a single file + * for modifications. Repurpose the filename for API compatibility. + * I'm not convinced this is a good thing, maybe it should go. + */ + filename = e->len ? e->name : basename_r(handle->filename); + + handle->cb(handle, filename, events, 0); + } + } + while (handle->fd != -1); /* handle might've been closed by callback */ +} + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + int flags; + int fd; + + /* + * TODO share a single inotify fd across the event loop? + * We'll run into fs.inotify.max_user_instances if we + * keep creating new inotify fds. + */ + if ((fd = new_inotify_fd()) == -1) { + uv_err_new(loop, errno); + return -1; + } + + flags = IN_ATTRIB + | IN_CREATE + | IN_MODIFY + | IN_DELETE + | IN_DELETE_SELF + | IN_MOVED_FROM + | IN_MOVED_TO; + + if (inotify_add_watch(fd, filename, flags) == -1) { + uv_err_new(loop, errno); + uv__close(fd); + return -1; + } + + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + handle->filename = strdup(filename); /* this should go! */ + handle->cb = cb; + handle->fd = fd; + + ev_io_init(&handle->read_watcher, uv__inotify_read, fd, EV_READ); + ev_io_start(loop->ev, &handle->read_watcher); + + return 0; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + ev_io_stop(handle->loop->ev, &handle->read_watcher); + uv__close(handle->fd); + handle->fd = -1; + free(handle->filename); +} diff --git a/deps/uv/src/unix/netbsd.c b/deps/uv/src/unix/netbsd.c index 91a286fee..0ba799975 100644 --- a/deps/uv/src/unix/netbsd.c +++ b/deps/uv/src/unix/netbsd.c @@ -20,17 +20,20 @@ #include "uv.h" +#include #include -#include +#include #include #include #include +#include #undef NANOSEC #define NANOSEC 1000000000 + uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -66,3 +69,17 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index 9ee6a3103..0b5c03b5a 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -22,8 +22,11 @@ #include #include -#include +#include +#include + #include +#include uint64_t uv_hrtime() { @@ -58,3 +61,17 @@ int uv_exepath(char* buffer, size_t* size) { *size = res; return (0); } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index 9de7dd9fc..88ce52a24 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -23,6 +23,7 @@ #include "internal.h" #include +#include #include #include @@ -71,3 +72,43 @@ fatal: int uv_is_tty(uv_file file) { return isatty(file); } + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + struct winsize ws; + + if (ioctl(tty->fd, TIOCGWINSZ, &ws) < 0) { + uv_err_new(tty->loop, errno); + return -1; + } + + *width = ws.ws_col; + *height = ws.ws_row; + + return 0; +} + + +uv_handle_type uv_guess_handle(uv_file file) { + struct stat s; + + if (file < 0) { + uv_err_new(NULL, EINVAL); /* XXX Need loop? */ + return -1; + } + + if (isatty(file)) { + return UV_TTY; + } + + if (fstat(file, &s)) { + uv_err_new(NULL, errno); /* XXX Need loop? */ + return -1; + } + + if (!S_ISSOCK(s.st_mode) && !S_ISFIFO(s.st_mode)) { + return UV_FILE; + } + + return UV_NAMED_PIPE; +} diff --git a/deps/uv/src/win/fs-event.c b/deps/uv/src/win/fs-event.c new file mode 100644 index 000000000..a68dfe00d --- /dev/null +++ b/deps/uv/src/win/fs-event.c @@ -0,0 +1,384 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "uv.h" +#include "internal.h" + + +const unsigned int uv_directory_watcher_buffer_size = 4096; + + +static void uv_fs_event_init_handle(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb) { + handle->type = UV_FS_EVENT; + handle->loop = loop; + handle->flags = 0; + handle->cb = cb; + handle->is_path_dir = 0; + handle->dir_handle = INVALID_HANDLE_VALUE; + handle->buffer = NULL; + handle->req_pending = 0; + handle->filew = NULL; + + uv_req_init(loop, (uv_req_t*)&handle->req); + handle->req.type = UV_FS_EVENT_REQ; + handle->req.data = (void*)handle; + + handle->filename = strdup(filename); + if (!handle->filename) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + loop->counters.handle_init++; + loop->counters.fs_event_init++; + + uv_ref(loop); +} + + +static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop, + uv_fs_event_t* handle) { + assert(handle->dir_handle != INVALID_HANDLE_VALUE); + assert(!handle->req_pending); + + memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped)); + if (!ReadDirectoryChangesW(handle->dir_handle, + handle->buffer, + uv_directory_watcher_buffer_size, + FALSE, + FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_LAST_ACCESS | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY, + NULL, + &handle->req.overlapped, + NULL)) { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(&handle->req, GetLastError()); + uv_insert_pending_req(loop, (uv_req_t*)&handle->req); + } + + handle->req_pending = 1; +} + + +static int uv_split_path(const wchar_t* filename, wchar_t** dir, + wchar_t** file) { + int len = wcslen(filename); + int i = len; + while (i > 0 && filename[--i] != '\\' && filename[i] != '/'); + + if (i == 0) { + *dir = (wchar_t*)malloc((MAX_PATH + 1) * sizeof(wchar_t)); + if (!*dir) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!GetCurrentDirectoryW(MAX_PATH, *dir)) { + free(*dir); + *dir = NULL; + return -1; + } + + *file = wcsdup(filename); + } else { + *dir = (wchar_t*)malloc((i + 1) * sizeof(wchar_t)); + if (!*dir) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + wcsncpy(*dir, filename, i); + (*dir)[i] = L'\0'; + + *file = (wchar_t*)malloc((len - i) * sizeof(wchar_t)); + if (!*file) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + wcsncpy(*file, filename + i + 1, len - i - 1); + (*file)[len - i - 1] = L'\0'; + } + + return 0; +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb) { + int name_size; + DWORD attr, last_error; + wchar_t* dir = NULL, *dir_to_watch, *filenamew; + + uv_fs_event_init_handle(loop, handle, filename, cb); + + /* Convert name to UTF16. */ + name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(wchar_t); + filenamew = (wchar_t*)malloc(name_size); + if (!filenamew) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf8_to_utf16(filename, filenamew, + name_size / sizeof(wchar_t))) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* Determine whether filename is a file or a directory. */ + attr = GetFileAttributesW(filenamew); + if (attr == INVALID_FILE_ATTRIBUTES) { + last_error = GetLastError(); + goto error; + } + + handle->is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; + + if (handle->is_path_dir) { + /* filename is a directory, so that's the directory that we will watch. */ + dir_to_watch = filenamew; + } else { + /* + * filename is a file. So we split filename into dir & file parts, and + * watch the dir directory. + */ + if (uv_split_path(filenamew, &dir, &handle->filew) != 0) { + last_error = GetLastError(); + goto error; + } + + dir_to_watch = dir; + } + + handle->dir_handle = CreateFileW(dir_to_watch, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_DELETE | + FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OVERLAPPED, + NULL); + + if (dir) { + free(dir); + dir = NULL; + } + + if (handle->dir_handle == INVALID_HANDLE_VALUE) { + last_error = GetLastError(); + goto error; + } + + if (CreateIoCompletionPort(handle->dir_handle, + loop->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + last_error = GetLastError(); + goto error; + } + + handle->buffer = (char*)_aligned_malloc(uv_directory_watcher_buffer_size, + sizeof(DWORD)); + if (!handle->buffer) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped)); + + if (!ReadDirectoryChangesW(handle->dir_handle, + handle->buffer, + uv_directory_watcher_buffer_size, + FALSE, + FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_LAST_ACCESS | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY, + NULL, + &handle->req.overlapped, + NULL)) { + last_error = GetLastError(); + goto error; + } + + handle->req_pending = 1; + return 0; + +error: + if (handle->filename) { + free(handle->filename); + handle->filename = NULL; + } + + if (handle->filew) { + free(handle->filew); + handle->filew = NULL; + } + + if (handle->dir_handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->dir_handle); + handle->dir_handle = INVALID_HANDLE_VALUE; + } + + if (handle->buffer) { + _aligned_free(handle->buffer); + handle->buffer = NULL; + } + + uv_set_sys_error(loop, last_error); + return -1; +} + + +void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, + uv_fs_event_t* handle) { + FILE_NOTIFY_INFORMATION* file_info; + char* filename = NULL; + int utf8size; + DWORD offset = 0; + + assert(req->type == UV_FS_EVENT_REQ); + assert(handle->req_pending); + handle->req_pending = 0; + + file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset); + + if (REQ_SUCCESS(req)) { + if (req->overlapped.InternalHigh > 0) { + do { + file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset); + + /* + * Fire the event only if we were asked to watch a directory, + * or if the filename filter matches. + */ + if (handle->is_path_dir || _wcsnicmp(handle->filew, file_info->FileName, + file_info->FileNameLength / sizeof(wchar_t)) == 0) { + + /* Convert the filename to utf8. */ + utf8size = uv_utf16_to_utf8(file_info->FileName, + file_info->FileNameLength / + sizeof(wchar_t), + NULL, + 0); + if (utf8size) { + filename = (char*)malloc(utf8size + 1); + if (!filename) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + utf8size = uv_utf16_to_utf8(file_info->FileName, + file_info->FileNameLength / + sizeof(wchar_t), + filename, + utf8size); + if (utf8size) { + filename[utf8size] = L'\0'; + } else { + free(filename); + filename = NULL; + } + } + + switch (file_info->Action) { + case FILE_ACTION_ADDED: + case FILE_ACTION_REMOVED: + case FILE_ACTION_RENAMED_OLD_NAME: + case FILE_ACTION_RENAMED_NEW_NAME: + handle->cb(handle, filename, UV_RENAME, 0); + break; + + case FILE_ACTION_MODIFIED: + handle->cb(handle, filename, UV_CHANGE, 0); + break; + } + + free(filename); + filename = NULL; + } + + offset = file_info->NextEntryOffset; + } while(offset); + } else { + handle->cb(handle, NULL, UV_CHANGE, 0); + } + } else { + loop->last_error = GET_REQ_UV_ERROR(req); + handle->cb(handle, NULL, 0, -1); + } + + if (!(handle->flags & UV_HANDLE_CLOSING)) { + uv_fs_event_queue_readdirchanges(loop, handle); + } else { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} + + +void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) { + if (handle->dir_handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->dir_handle); + handle->dir_handle = INVALID_HANDLE_VALUE; + } + + if (!handle->req_pending) { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} + + +void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING && + !handle->req_pending) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->buffer) { + _aligned_free(handle->buffer); + handle->buffer = NULL; + } + + if (handle->filew) { + free(handle->filew); + handle->filew = NULL; + } + + if (handle->filename) { + free(handle->filename); + handle->filename = NULL; + } + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} diff --git a/deps/uv/src/win/handle.c b/deps/uv/src/win/handle.c index 3d32a403e..ab4f64bc5 100644 --- a/deps/uv/src/win/handle.c +++ b/deps/uv/src/win/handle.c @@ -120,6 +120,10 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { uv_process_close(loop, process); return; + case UV_FS_EVENT: + uv_fs_event_close(loop, (uv_fs_event_t*)handle); + return; + default: /* Not supported */ abort(); @@ -177,6 +181,10 @@ void uv_process_endgames(uv_loop_t* loop) { uv_process_endgame(loop, (uv_process_t*) handle); break; + case UV_FS_EVENT: + uv_fs_event_endgame(loop, (uv_fs_event_t*) handle); + break; + default: assert(0); break; diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index ee1834ed6..87a64eda1 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -231,6 +231,14 @@ void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req); void uv_process_work_req(uv_loop_t* loop, uv_work_t* req); +/* + * FS Event + */ +void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle); +void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle); +void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle); + + /* * Error handling */ diff --git a/deps/uv/src/win/req.c b/deps/uv/src/win/req.c index beff95bd0..a0a6e03dc 100644 --- a/deps/uv/src/win/req.c +++ b/deps/uv/src/win/req.c @@ -165,6 +165,10 @@ void uv_process_reqs(uv_loop_t* loop) { uv_process_work_req(loop, (uv_work_t*) req); break; + case UV_FS_EVENT_REQ: + uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); + break; + default: assert(0); } diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index c451a99d1..dc477c76c 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -42,3 +42,15 @@ int uv_is_tty(uv_file file) { int r = GetConsoleMode((HANDLE)_get_osfhandle(file), &result); return r ? 1 : 0; } + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + assert(0 && "implement me"); + return -1; +} + + +uv_handle_type uv_guess_handle(uv_file file) { + assert(0 && "implement me"); + return UV_UNKNOWN_HANDLE; +} diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c new file mode 100644 index 000000000..d249ec669 --- /dev/null +++ b/deps/uv/test/test-fs-event.c @@ -0,0 +1,217 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include + +uv_fs_event_t fs_event; +uv_timer_t timer; +int timer_cb_called; +int close_cb_called; +int fs_event_cb_called; + +static void create_dir(uv_loop_t* loop, const char* name) { + int r; + uv_fs_t req; + r = uv_fs_mkdir(loop, &req, name, 0755, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void create_file(uv_loop_t* loop, const char* name) { + int r; + uv_file file; + uv_fs_t req; + + r = uv_fs_open(loop, &req, name, O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + file = r; + uv_fs_req_cleanup(&req); + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void touch_file(uv_loop_t* loop, const char* name) { + int r; + uv_file file; + uv_fs_t req; + + r = uv_fs_open(loop, &req, name, O_RDWR, 0, NULL); + ASSERT(r != -1); + file = r; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, "foo", 4, -1, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&req); + + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&req); +} + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + +static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_RENAME); + ASSERT(strcmp(filename, "file1") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + ASSERT(strcmp(filename, "file2") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, + const char* filename, int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + ASSERT(strcmp(filename, "watch_file") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void timer_cb_dir(uv_timer_t* handle, int status) { + ++timer_cb_called; + create_file(handle->loop, "watch_dir/file1"); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void timer_cb_file(uv_timer_t* handle, int status) { + ++timer_cb_called; + + if (timer_cb_called == 1) { + touch_file(handle->loop, "watch_dir/file1"); + } else { + touch_file(handle->loop, "watch_dir/file2"); + uv_close((uv_handle_t*)handle, close_cb); + } +} + +TEST_IMPL(fs_event_watch_dir) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + create_dir(loop, "watch_dir"); + + r = uv_fs_event_init(loop, &fs_event, "watch_dir", fs_event_cb_dir); + ASSERT(r != -1); + r = uv_timer_init(loop, &timer); + ASSERT(r != -1); + r = uv_timer_start(&timer, timer_cb_dir, 100, 0); + ASSERT(r != -1); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + r = uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + + return 0; +} + +TEST_IMPL(fs_event_watch_file) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + create_dir(loop, "watch_dir"); + create_file(loop, "watch_dir/file1"); + create_file(loop, "watch_dir/file2"); + + r = uv_fs_event_init(loop, &fs_event, "watch_dir/file2", fs_event_cb_file); + ASSERT(r != -1); + r = uv_timer_init(loop, &timer); + ASSERT(r != -1); + r = uv_timer_start(&timer, timer_cb_file, 100, 100); + ASSERT(r != -1); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(timer_cb_called == 2); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + r = uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + + return 0; +} + +TEST_IMPL(fs_event_watch_file_current_dir) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_file", NULL); + create_file(loop, "watch_file"); + + r = uv_fs_event_init(loop, &fs_event, "watch_file", + fs_event_cb_file_current_dir); + ASSERT(r != -1); + + touch_file(loop, "watch_file"); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(close_cb_called == 1); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_file", NULL); + return 0; +} diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 429773f27..bf376e391 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -87,6 +87,9 @@ TEST_DECLARE (fs_link) TEST_DECLARE (fs_symlink) TEST_DECLARE (fs_utime) TEST_DECLARE (fs_futime) +TEST_DECLARE (fs_event_watch_dir) +TEST_DECLARE (fs_event_watch_file) +TEST_DECLARE (fs_event_watch_file_current_dir) TEST_DECLARE (threadpool_queue_work_simple) #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) @@ -201,6 +204,9 @@ TASK_LIST_START TEST_ENTRY (fs_utime) TEST_ENTRY (fs_futime) TEST_ENTRY (fs_symlink) + TEST_ENTRY (fs_event_watch_dir) + TEST_ENTRY (fs_event_watch_file) + TEST_ENTRY (fs_event_watch_file_current_dir) TEST_ENTRY (threadpool_queue_work_simple) diff --git a/deps/uv/test/test-tty.c b/deps/uv/test/test-tty.c index 4f9fd5468..f41c5e543 100644 --- a/deps/uv/test/test-tty.c +++ b/deps/uv/test/test-tty.c @@ -23,10 +23,35 @@ #include "task.h" TEST_IMPL(tty) { + int r, width, height; + uv_tty_t tty; uv_loop_t* loop = uv_default_loop(); + /* + * Not necessarally a problem if this assert goes off. E.G you are piping + * this test to a file. 0 == stdin. + */ ASSERT(uv_is_tty(0) == 1); + r = uv_tty_init(uv_default_loop(), &tty, 0); + ASSERT(r == 0); + + ASSERT(UV_TTY == uv_guess_handle(0)); + + r = uv_tty_get_winsize(&tty, &width, &height); + ASSERT(r == 0); + + printf("width=%d height=%d\n", width, height); + + /* + * Is it a safe assumption that most people have terminals larger than + * 10x10? + */ + ASSERT(width > 10); + ASSERT(height > 10); + + uv_close((uv_handle_t*)&tty, NULL); + uv_run(loop); return 0; diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 6600a2f51..e32dddd3b 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -103,6 +103,7 @@ 'src/win/core.c', 'src/win/error.c', 'src/win/fs.c', + 'src/win/fs-event.c', 'src/win/getaddrinfo.c', 'src/win/handle.c', 'src/win/internal.h', @@ -238,6 +239,7 @@ 'test/test-delayed-accept.c', 'test/test-fail-always.c', 'test/test-fs.c', + 'test/test-fs-event.c', 'test/test-get-currentexe.c', 'test/test-getaddrinfo.c', 'test/test-gethostbyname.c', @@ -334,3 +336,4 @@ ] } + -- cgit v1.2.3