diff --git a/config-unix.mk b/config-unix.mk index 6ff4574b..3453132a 100644 --- a/config-unix.mk +++ b/config-unix.mk @@ -54,6 +54,7 @@ EIO_CONFIG=config_darwin.h CPPFLAGS += -Isrc/ares/config_darwin LINKFLAGS+=-framework CoreServices OBJS += src/unix/darwin.o +OBJS += src/unix/kqueue.o endif ifeq (Linux,$(uname_S)) @@ -71,6 +72,7 @@ EIO_CONFIG=config_freebsd.h CPPFLAGS += -Isrc/ares/config_freebsd LINKFLAGS+= OBJS += src/unix/freebsd.o +OBJS += src/unix/kqueue.o endif ifeq (NetBSD,$(uname_S)) @@ -79,6 +81,7 @@ EIO_CONFIG=config_netbsd.h CPPFLAGS += -Isrc/ares/config_netbsd LINKFLAGS+= OBJS += src/unix/netbsd.o +OBJS += src/unix/kqueue.o endif ifneq (,$(findstring CYGWIN,$(uname_S))) diff --git a/include/uv-private/ev.h b/include/uv-private/ev.h index 1db1e266..5d2d7a1e 100644 --- a/include/uv-private/ev.h +++ b/include/uv-private/ev.h @@ -207,6 +207,7 @@ enum { EV_NONE = 0x00, /* no events */ EV_READ = 0x01, /* ev_io detected read will not block */ EV_WRITE = 0x02, /* ev_io detected write will not block */ + EV_LIBUV_KQUEUE_HACK = 0x40, EV__IOFDSET = 0x80, /* internal use only */ EV_IO = EV_READ, /* alias for type-detection */ EV_TIMER = 0x00000100, /* timer timed out */ diff --git a/include/uv-private/uv-linux.h b/include/uv-private/uv-linux.h deleted file mode 100644 index 7f7b0593..00000000 --- a/include/uv-private/uv-linux.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#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/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index be2abcd9..1b6d86df 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -27,10 +27,6 @@ #include "ev.h" #include "eio.h" -#if defined(__linux__) -#include "uv-private/uv-linux.h" -#endif - #include #include #include @@ -47,11 +43,6 @@ 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; \ /* \ @@ -188,4 +179,28 @@ typedef int uv_file; struct termios orig_termios; \ int mode; +/* UV_FS_EVENT_PRIVATE_FIELDS */ +#if defined(__linux__) + +#define UV_FS_EVENT_PRIVATE_FIELDS \ + ev_io read_watcher; \ + uv_fs_event_cb cb; \ + +#elif (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) \ + || defined(__FreeBSD__) \ + || defined(__OpenBSD__) \ + || defined(__NetBSD__) + +#define UV_FS_EVENT_PRIVATE_FIELDS \ + ev_io event_watcher; \ + uv_fs_event_cb cb; \ + int fflags; \ + +#else + +/* Stub for platforms where the file watcher isn't implemented yet. */ +#define UV_FS_EVENT_PRIVATE_FIELDS + +#endif + #endif /* UV_UNIX_H */ diff --git a/src/unix/core.c b/src/unix/core.c index 8c065ad2..719327a9 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -172,7 +172,7 @@ void uv_loop_delete(uv_loop_t* loop) { uv_loop_t* uv_default_loop() { if (!default_loop_ptr) { default_loop_ptr = &default_loop_struct; -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 +#if HAVE_KQUEUE default_loop_struct.ev = ev_default_loop(EVBACKEND_KQUEUE); #else default_loop_struct.ev = ev_default_loop(EVFLAG_AUTO); diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 26016569..ad0ab191 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -106,16 +106,3 @@ void uv_loadavg(double avg[3]) { avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } - -int uv_fs_event_init(uv_loop_t* loop, - uv_fs_event_t* handle, - const char* filename, - uv_fs_event_cb cb) { - uv__set_sys_error(loop, ENOSYS); - return -1; -} - - -void uv__fs_event_destroy(uv_fs_event_t* handle) { - assert(0 && "implement me"); -} diff --git a/src/unix/ev/ev.c b/src/unix/ev/ev.c index 5e616c25..a3bec43f 100644 --- a/src/unix/ev/ev.c +++ b/src/unix/ev/ev.c @@ -2663,7 +2663,8 @@ ev_io_start (EV_P_ ev_io *w) return; assert (("libev: ev_io_start called with negative fd", fd >= 0)); - assert (("libev: ev_io_start called with illegal event mask", !(w->events & ~(EV__IOFDSET | EV_READ | EV_WRITE)))); + assert (("libev: ev_io_start called with illegal event mask", + !(w->events & ~(EV__IOFDSET | EV_READ | EV_WRITE | EV_LIBUV_KQUEUE_HACK)))); EV_FREQUENT_CHECK; diff --git a/src/unix/ev/ev_kqueue.c b/src/unix/ev/ev_kqueue.c index 1b526d1c..212ca29a 100644 --- a/src/unix/ev/ev_kqueue.c +++ b/src/unix/ev/ev_kqueue.c @@ -43,6 +43,9 @@ #include #include +extern void +uv__kqueue_hack (EV_P_ int fflags, ev_io *w); + void inline_speed kqueue_change (EV_P_ int fd, int filter, int flags, int fflags) { @@ -80,6 +83,10 @@ kqueue_modify (EV_P_ int fd, int oev, int nev) if (nev & EV_WRITE) kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, NOTE_EOF); + + if (nev & EV_LIBUV_KQUEUE_HACK) + kqueue_change (EV_A_ fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, + NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE); } static void @@ -114,6 +121,13 @@ kqueue_poll (EV_P_ ev_tstamp timeout) { int fd = kqueue_events [i].ident; + if (kqueue_events [i].filter == EVFILT_VNODE) + { + /* pass kqueue filter flags to libuv */ + ev_io *w = (ev_io *)(anfds [fd].head); + uv__kqueue_hack (EV_A_ kqueue_events [i].fflags, w); + } + if (expect_false (kqueue_events [i].flags & EV_ERROR)) { int err = kqueue_events [i].data; @@ -140,6 +154,7 @@ kqueue_poll (EV_P_ ev_tstamp timeout) fd, kqueue_events [i].filter == EVFILT_READ ? EV_READ : kqueue_events [i].filter == EVFILT_WRITE ? EV_WRITE + : kqueue_events [i].filter == EVFILT_VNODE ? EV_LIBUV_KQUEUE_HACK : 0 ); } diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index e173fd83..49aeb95e 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -104,16 +104,3 @@ void uv_loadavg(double avg[3]) { avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } - -int uv_fs_event_init(uv_loop_t* loop, - uv_fs_event_t* handle, - const char* filename, - uv_fs_event_cb cb) { - uv__set_sys_error(loop, ENOSYS); - return -1; -} - - -void uv__fs_event_destroy(uv_fs_event_t* handle) { - assert(0 && "implement me"); -} diff --git a/src/unix/internal.h b/src/unix/internal.h index a10cd3d0..7200e33d 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -32,33 +32,37 @@ #include #include -#undef HAVE_FUTIMES -#undef HAVE_PIPE2 -#undef HAVE_ACCEPT4 - /* futimes() requires linux >= 2.6.22 and glib >= 2.6 */ #if LINUX_VERSION_CODE >= 0x20616 && __GLIBC_PREREQ(2, 6) -#define HAVE_FUTIMES +#define HAVE_FUTIMES 1 #endif /* pipe2() requires linux >= 2.6.27 and glibc >= 2.9 */ #if LINUX_VERSION_CODE >= 0x2061B && __GLIBC_PREREQ(2, 9) -#define HAVE_PIPE2 +#define HAVE_PIPE2 1 #endif /* accept4() requires linux >= 2.6.28 and glib >= 2.10 */ #if LINUX_VERSION_CODE >= 0x2061C && __GLIBC_PREREQ(2, 10) -#define HAVE_ACCEPT4 +#define HAVE_ACCEPT4 1 #endif #endif /* __linux__ */ #ifdef __APPLE__ -# define HAVE_FUTIMES +# define HAVE_FUTIMES 1 #endif #ifdef __FreeBSD__ -# define HAVE_FUTIMES +# define HAVE_FUTIMES 1 +#endif + +/* FIXME exact copy of the #ifdef guard in uv-unix.h */ +#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) \ + || defined(__FreeBSD__) \ + || defined(__OpenBSD__) \ + || defined(__NetBSD__) +# define HAVE_KQUEUE 1 #endif #define container_of(ptr, type, member) \ diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c new file mode 100644 index 00000000..924865bd --- /dev/null +++ b/src/unix/kqueue.c @@ -0,0 +1,121 @@ +/* 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 +#include + +#include +#include +#include +#include +#include + +static void uv__fs_event(EV_P_ ev_io* w, int revents); + + +static void uv__fs_event_start(uv_fs_event_t* handle) { + ev_io_init(&handle->event_watcher, + uv__fs_event, + handle->fd, + EV_LIBUV_KQUEUE_HACK); + ev_io_start(handle->loop->ev, &handle->event_watcher); +} + + +static void uv__fs_event_stop(uv_fs_event_t* handle) { + ev_io_stop(handle->loop->ev, &handle->event_watcher); +} + + +static void uv__fs_event(EV_P_ ev_io* w, int revents) { + uv_fs_event_t* handle; + int events; + + assert(revents == EV_LIBUV_KQUEUE_HACK); + + handle = container_of(w, uv_fs_event_t, event_watcher); + + if (handle->fflags & (NOTE_ATTRIB | NOTE_EXTEND)) + events = UV_CHANGE; + else + events = UV_RENAME; + + handle->cb(handle, NULL, events, 0); + + uv__fs_event_stop(handle); + + /* File watcher operates in one-shot mode, re-arm it. */ + if (handle->fd != -1) + uv__fs_event_start(handle); +} + + +/* Called by libev, don't touch. */ +void uv__kqueue_hack(EV_P_ int fflags, ev_io *w) { + uv_fs_event_t* handle; + + handle = container_of(w, uv_fs_event_t, event_watcher); + handle->fflags = fflags; +} + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { +#if HAVE_KQUEUE + int fd; + + if (cb == NULL) { + uv__set_sys_error(loop, EINVAL); + return -1; + } + + /* TODO open asynchronously - but how do we report back errors? */ + if ((fd = open(filename, O_RDONLY)) == -1) { + uv__set_sys_error(loop, errno); + return -1; + } + + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + handle->filename = strdup(filename); + handle->fflags = 0; + handle->cb = cb; + handle->fd = fd; + uv__fs_event_start(handle); + + return 0; +#else + uv__set_sys_error(loop, ENOSYS); + return -1; +#endif +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + free(handle->filename); + uv__close(handle->fd); + handle->fd = -1; +} diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c index 37bb87ee..ff42e8ce 100644 --- a/src/unix/netbsd.c +++ b/src/unix/netbsd.c @@ -110,16 +110,3 @@ double uv_get_total_memory(void) { return (double) info; } - -int uv_fs_event_init(uv_loop_t* loop, - uv_fs_event_t* handle, - const char* filename, - uv_fs_event_cb cb) { - uv__set_sys_error(loop, ENOSYS); - return -1; -} - - -void uv__fs_event_destroy(uv_fs_event_t* handle) { - assert(0 && "implement me"); -} diff --git a/test/test-fs-event.c b/test/test-fs-event.c index d249ec66..724000db 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -25,11 +25,12 @@ #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 uv_fs_event_t fs_event; +static uv_timer_t timer; +static int timer_cb_called; +static int close_cb_called; +static int fs_event_cb_called; +static int timer_cb_touch_called; static void create_dir(uv_loop_t* loop, const char* name) { int r; @@ -84,7 +85,7 @@ 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(strcmp(filename, "file1") == 0); + ASSERT(filename == NULL || strcmp(filename, "file1") == 0); uv_close((uv_handle_t*)handle, close_cb); } @@ -94,7 +95,7 @@ static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_CHANGE); - ASSERT(strcmp(filename, "file2") == 0); + ASSERT(filename == NULL || strcmp(filename, "file2") == 0); uv_close((uv_handle_t*)handle, close_cb); } @@ -104,7 +105,7 @@ static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, ASSERT(handle == &fs_event); ASSERT(status == 0); ASSERT(events == UV_CHANGE); - ASSERT(strcmp(filename, "watch_file") == 0); + ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); uv_close((uv_handle_t*)handle, close_cb); } @@ -125,6 +126,13 @@ static void timer_cb_file(uv_timer_t* handle, int status) { } } +static void timer_cb_touch(uv_timer_t* timer, int status) { + ASSERT(status == 0); + uv_close((uv_handle_t*)timer, NULL); + touch_file(timer->loop, "watch_file"); + timer_cb_touch_called++; +} + TEST_IMPL(fs_event_watch_dir) { uv_fs_t fs_req; uv_loop_t* loop = uv_default_loop(); @@ -192,10 +200,13 @@ TEST_IMPL(fs_event_watch_file) { } TEST_IMPL(fs_event_watch_file_current_dir) { + uv_timer_t timer; + uv_loop_t* loop; uv_fs_t fs_req; - uv_loop_t* loop = uv_default_loop(); int r; + loop = uv_default_loop(); + /* Setup */ uv_fs_unlink(loop, &fs_req, "watch_file", NULL); create_file(loop, "watch_file"); @@ -203,11 +214,20 @@ TEST_IMPL(fs_event_watch_file_current_dir) { r = uv_fs_event_init(loop, &fs_event, "watch_file", fs_event_cb_file_current_dir); ASSERT(r != -1); - - touch_file(loop, "watch_file"); + + r = uv_timer_init(loop, &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb_touch, 1, 0); + ASSERT(r == 0); + + ASSERT(timer_cb_touch_called == 0); + ASSERT(fs_event_cb_called == 0); + ASSERT(close_cb_called == 0); uv_run(loop); + ASSERT(timer_cb_touch_called == 1); ASSERT(fs_event_cb_called == 1); ASSERT(close_cb_called == 1); diff --git a/uv.gyp b/uv.gyp index 5bf8c065..2b2764d2 100644 --- a/uv.gyp +++ b/uv.gyp @@ -229,6 +229,9 @@ 'EIO_CONFIG_H="config_freebsd.h"', ], }], + [ 'OS=="mac" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd"', { + 'sources': [ 'src/unix/kqueue.c' ], + }], ] },