diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index b13e75c5..3618f469 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -316,10 +316,21 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { ctx.release = NULL; ctx.copyDescription = NULL; - latency = 0.15; + latency = 0.05; - /* Set appropriate flags */ - flags = kFSEventStreamCreateFlagFileEvents; + /* Explanation of selected flags: + * 1. NoDefer - without this flag, events that are happening continuously + * (i.e. each event is happening after time interval less than `latency`, + * counted from previous event), will be deferred and passed to callback + * once they'll either fill whole OS buffer, or when this continuous stream + * will stop (i.e. there'll be delay between events, bigger than + * `latency`). + * Specifying this flag will invoke callback after `latency` time passed + * since event. + * 2. FileEvents - fire callback for file changes too (by default it is firing + * it only for directory changes). + */ + flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents; /* * NOTE: It might sound like a good idea to remember last seen StreamEventId, diff --git a/src/unix/internal.h b/src/unix/internal.h index 7bfb9a20..110978e3 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -241,6 +241,7 @@ void uv__fsevents_loop_delete(uv_loop_t* loop); /* OSX < 10.7 has no file events, polyfill them */ #ifndef MAC_OS_X_VERSION_10_7 +static const int kFSEventStreamCreateFlagNoDefer = 0x00000002; static const int kFSEventStreamCreateFlagFileEvents = 0x00000010; static const int kFSEventStreamEventFlagItemCreated = 0x00000100; static const int kFSEventStreamEventFlagItemRemoved = 0x00000200; diff --git a/test/test-fs-event.c b/test/test-fs-event.c index 66132e17..8dc0c3d3 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -36,11 +36,21 @@ #endif static uv_fs_event_t fs_event; +static const char file_prefix[] = "fsevent-"; static uv_timer_t timer; -static int timer_cb_called = 0; -static int close_cb_called = 0; -static int fs_event_cb_called = 0; -static int timer_cb_touch_called = 0; +static int timer_cb_called; +static int close_cb_called; +static const int fs_event_file_count = 128; +static int fs_event_created; +static int fs_event_cb_called; +#if defined(PATH_MAX) +static char fs_event_filename[PATH_MAX]; +#else +static char fs_event_filename[1024]; +#endif /* defined(PATH_MAX) */ +static int timer_cb_touch_called; + +static void fs_event_unlink_files(uv_timer_t* handle, int status); static void create_dir(uv_loop_t* loop, const char* name) { int r; @@ -107,6 +117,69 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, uv_close((uv_handle_t*)handle, close_cb); } +static void fs_event_cb_dir_multi_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_RENAME); + ASSERT(filename == NULL || + strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); + + /* Stop watching dir when received events about all files: + * both create and close events */ + if (fs_event_cb_called == 2 * fs_event_file_count) { + ASSERT(0 == uv_fs_event_stop(handle)); + uv_close((uv_handle_t*) handle, close_cb); + } +} + +static const char* fs_event_get_filename(int i) { + snprintf(fs_event_filename, + sizeof(fs_event_filename), + "watch_dir/%s%d", + file_prefix, + i); + return fs_event_filename; +} + +static void fs_event_create_files(uv_timer_t* handle, int status) { + int i; + + /* Already created all files */ + if (fs_event_created == fs_event_file_count) { + uv_close((uv_handle_t*) &timer, close_cb); + return; + } + + /* Create all files */ + for (i = 0; i < 16; i++, fs_event_created++) + create_file(handle->loop, fs_event_get_filename(i)); + + /* And unlink them */ + ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 50, 0)); +} + +void fs_event_unlink_files(uv_timer_t* handle, int status) { + int r; + int i; + + /* NOTE: handle might be NULL if invoked not as timer callback */ + + /* Unlink all files */ + for (i = 0; i < 16; i++) { + r = remove(fs_event_get_filename(i)); + if (handle != NULL) + ASSERT(r == 0); + } + + /* And create them again */ + if (handle != NULL) + ASSERT(0 == uv_timer_start(&timer, fs_event_create_files, 50, 0)); +} + static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, int events, int status) { ++fs_event_cb_called; @@ -148,12 +221,6 @@ static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, } } -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; @@ -184,6 +251,7 @@ TEST_IMPL(fs_event_watch_dir) { int r; /* Setup */ + fs_event_unlink_files(NULL, 0); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/"); @@ -191,20 +259,21 @@ TEST_IMPL(fs_event_watch_dir) { r = uv_fs_event_init(loop, &fs_event); ASSERT(r == 0); - r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_dir", 0); + r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0); ASSERT(r == 0); r = uv_timer_init(loop, &timer); ASSERT(r == 0); - r = uv_timer_start(&timer, timer_cb_dir, 100, 0); + r = uv_timer_start(&timer, fs_event_create_files, 100, 0); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_event_cb_called == 1); - ASSERT(timer_cb_called == 1); + ASSERT(fs_event_cb_called == 2 * fs_event_file_count); + ASSERT(fs_event_created == fs_event_file_count); ASSERT(close_cb_called == 2); /* Cleanup */ + fs_event_unlink_files(NULL, 0); remove("watch_dir/file2"); remove("watch_dir/file1"); remove("watch_dir/");