win: add support for recursive file watching
Original patch by @ghostoy, modified by @bpasero and yours truly. Refs: https://github.com/joyent/libuv/pull/1473 Refs: https://github.com/libuv/libuv/pull/198 PR-URL: https://github.com/libuv/libuv/pull/421 Reviewed-By: Bert Belder <bertbelder@gmail.com>
This commit is contained in:
parent
f3613e5082
commit
188e0e94ac
@ -88,7 +88,7 @@ API
|
||||
`path` for changes. `flags` can be an ORed mask of :c:type:`uv_fs_event_flags`.
|
||||
|
||||
.. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and
|
||||
only on OSX.
|
||||
only on OSX and Windows.
|
||||
|
||||
.. c:function:: int uv_fs_event_stop(uv_fs_event_t* handle)
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
|
||||
if (!ReadDirectoryChangesW(handle->dir_handle,
|
||||
handle->buffer,
|
||||
uv_directory_watcher_buffer_size,
|
||||
FALSE,
|
||||
(handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
@ -63,6 +63,20 @@ static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
|
||||
handle->req_pending = 1;
|
||||
}
|
||||
|
||||
static int uv_relative_path(const WCHAR* filename,
|
||||
const WCHAR* dir,
|
||||
WCHAR** relpath) {
|
||||
int dirlen = wcslen(dir);
|
||||
int filelen = wcslen(filename);
|
||||
if (dir[dirlen - 1] == '\\')
|
||||
dirlen--;
|
||||
*relpath = uv__malloc((MAX_PATH + 1) * sizeof(WCHAR));
|
||||
if (!*relpath)
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
wcsncpy(*relpath, filename + dirlen + 1, filelen - dirlen - 1);
|
||||
(*relpath)[filelen - dirlen - 1] = L'\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uv_split_path(const WCHAR* filename, WCHAR** dir,
|
||||
WCHAR** file) {
|
||||
@ -237,7 +251,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
|
||||
if (!ReadDirectoryChangesW(handle->dir_handle,
|
||||
handle->buffer,
|
||||
uv_directory_watcher_buffer_size,
|
||||
FALSE,
|
||||
(flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
@ -410,7 +424,9 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
|
||||
|
||||
if (long_filenamew) {
|
||||
/* Get the file name out of the long path. */
|
||||
result = uv_split_path(long_filenamew, NULL, &filenamew);
|
||||
result = uv_relative_path(long_filenamew,
|
||||
handle->dirw,
|
||||
&filenamew);
|
||||
uv__free(long_filenamew);
|
||||
|
||||
if (result == 0) {
|
||||
|
||||
@ -64,7 +64,7 @@ extern UV_THREAD_LOCAL int uv__crt_assert_enabled;
|
||||
|
||||
/* Used by all handles. */
|
||||
#define UV_HANDLE_CLOSED 0x00000002
|
||||
#define UV_HANDLE_ENDGAME_QUEUED 0x00000004
|
||||
#define UV_HANDLE_ENDGAME_QUEUED 0x00000008
|
||||
|
||||
/* uv-common.h: #define UV__HANDLE_CLOSING 0x00000001 */
|
||||
/* uv-common.h: #define UV__HANDLE_ACTIVE 0x00000040 */
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
|
||||
static uv_fs_event_t fs_event;
|
||||
static const char file_prefix[] = "fsevent-";
|
||||
static const char file_prefix_in_subdir[] = "subdir";
|
||||
static uv_timer_t timer;
|
||||
static int timer_cb_called;
|
||||
static int close_cb_called;
|
||||
@ -51,6 +52,7 @@ static char fs_event_filename[1024];
|
||||
static int timer_cb_touch_called;
|
||||
|
||||
static void fs_event_unlink_files(uv_timer_t* handle);
|
||||
static void fs_event_unlink_files_in_subdir(uv_timer_t* handle);
|
||||
|
||||
static void create_dir(uv_loop_t* loop, const char* name) {
|
||||
int r;
|
||||
@ -138,6 +140,25 @@ static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle,
|
||||
}
|
||||
}
|
||||
|
||||
static void fs_event_cb_dir_multi_file_in_subdir(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 || events == UV_CHANGE);
|
||||
ASSERT(filename == NULL ||
|
||||
strncmp(filename, file_prefix_in_subdir, sizeof(file_prefix_in_subdir) - 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),
|
||||
@ -147,6 +168,15 @@ static const char* fs_event_get_filename(int i) {
|
||||
return fs_event_filename;
|
||||
}
|
||||
|
||||
static const char* fs_event_get_filename_in_subdir(int i) {
|
||||
snprintf(fs_event_filename,
|
||||
sizeof(fs_event_filename),
|
||||
"watch_dir/subdir/%s%d",
|
||||
file_prefix,
|
||||
i);
|
||||
return fs_event_filename;
|
||||
}
|
||||
|
||||
static void fs_event_create_files(uv_timer_t* handle) {
|
||||
int i;
|
||||
|
||||
@ -164,6 +194,41 @@ static void fs_event_create_files(uv_timer_t* handle) {
|
||||
ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 50, 0));
|
||||
}
|
||||
|
||||
static void fs_event_create_files_in_subdir(uv_timer_t* handle) {
|
||||
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_in_subdir(i));
|
||||
|
||||
/* And unlink them */
|
||||
ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 50, 0));
|
||||
}
|
||||
|
||||
void fs_event_unlink_files_in_subdir(uv_timer_t* handle) {
|
||||
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_in_subdir(i));
|
||||
if (handle != NULL)
|
||||
ASSERT(r == 0);
|
||||
}
|
||||
|
||||
/* And create them again */
|
||||
if (handle != NULL)
|
||||
ASSERT(0 == uv_timer_start(&timer, fs_event_create_files_in_subdir, 50, 0));
|
||||
}
|
||||
|
||||
void fs_event_unlink_files(uv_timer_t* handle) {
|
||||
int r;
|
||||
int i;
|
||||
@ -282,6 +347,51 @@ TEST_IMPL(fs_event_watch_dir) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_IMPL(fs_event_watch_dir_recursive) {
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
uv_loop_t* loop;
|
||||
int r;
|
||||
|
||||
/* Setup */
|
||||
loop = uv_default_loop();
|
||||
fs_event_unlink_files(NULL);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/subdir");
|
||||
remove("watch_dir/");
|
||||
create_dir(loop, "watch_dir");
|
||||
create_dir(loop, "watch_dir/subdir");
|
||||
|
||||
r = uv_fs_event_init(loop, &fs_event);
|
||||
ASSERT(r == 0);
|
||||
r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file_in_subdir, "watch_dir", UV_FS_EVENT_RECURSIVE);
|
||||
ASSERT(r == 0);
|
||||
r = uv_timer_init(loop, &timer);
|
||||
ASSERT(r == 0);
|
||||
r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0);
|
||||
ASSERT(r == 0);
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
|
||||
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_in_subdir(NULL);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/subdir");
|
||||
remove("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
#else
|
||||
RETURN_SKIP("Recursive directory watching not supported on this platform.");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(fs_event_watch_file) {
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
int r;
|
||||
|
||||
@ -258,6 +258,7 @@ TEST_DECLARE (fs_file_open_append)
|
||||
TEST_DECLARE (fs_stat_missing_path)
|
||||
TEST_DECLARE (fs_read_file_eof)
|
||||
TEST_DECLARE (fs_event_watch_dir)
|
||||
TEST_DECLARE (fs_event_watch_dir_recursive)
|
||||
TEST_DECLARE (fs_event_watch_file)
|
||||
TEST_DECLARE (fs_event_watch_file_twice)
|
||||
TEST_DECLARE (fs_event_watch_file_current_dir)
|
||||
@ -664,6 +665,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (fs_read_file_eof)
|
||||
TEST_ENTRY (fs_file_open_append)
|
||||
TEST_ENTRY (fs_event_watch_dir)
|
||||
TEST_ENTRY (fs_event_watch_dir_recursive)
|
||||
TEST_ENTRY (fs_event_watch_file)
|
||||
TEST_ENTRY (fs_event_watch_file_twice)
|
||||
TEST_ENTRY (fs_event_watch_file_current_dir)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user