Skip to content

Commit 2ca3637

Browse files
saghulskomski
authored andcommitted
win: add support for recursive file watching
Original patch by @ghostoy, modified by @bpasero and yours truly. Refs: joyent/libuv#1473 Refs: libuv#198 PR-URL: libuv#421 Reviewed-By: Bert Belder <bertbelder@gmail.com>
1 parent d8b2d26 commit 2ca3637

5 files changed

Lines changed: 133 additions & 5 deletions

File tree

docs/src/fs_event.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ API
8888
`path` for changes. `flags` can be an ORed mask of :c:type:`uv_fs_event_flags`.
8989
9090
.. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and
91-
only on OSX.
91+
only on OSX and Windows.
9292
9393
.. c:function:: int uv_fs_event_stop(uv_fs_event_t* handle)
9494

src/win/fs-event.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
4343
if (!ReadDirectoryChangesW(handle->dir_handle,
4444
handle->buffer,
4545
uv_directory_watcher_buffer_size,
46-
FALSE,
46+
(handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
4747
FILE_NOTIFY_CHANGE_FILE_NAME |
4848
FILE_NOTIFY_CHANGE_DIR_NAME |
4949
FILE_NOTIFY_CHANGE_ATTRIBUTES |
@@ -63,6 +63,20 @@ static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
6363
handle->req_pending = 1;
6464
}
6565

66+
static int uv_relative_path(const WCHAR* filename,
67+
const WCHAR* dir,
68+
WCHAR** relpath) {
69+
int dirlen = wcslen(dir);
70+
int filelen = wcslen(filename);
71+
if (dir[dirlen - 1] == '\\')
72+
dirlen--;
73+
*relpath = uv__malloc((MAX_PATH + 1) * sizeof(WCHAR));
74+
if (!*relpath)
75+
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
76+
wcsncpy(*relpath, filename + dirlen + 1, filelen - dirlen - 1);
77+
(*relpath)[filelen - dirlen - 1] = L'\0';
78+
return 0;
79+
}
6680

6781
static int uv_split_path(const WCHAR* filename, WCHAR** dir,
6882
WCHAR** file) {
@@ -237,7 +251,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
237251
if (!ReadDirectoryChangesW(handle->dir_handle,
238252
handle->buffer,
239253
uv_directory_watcher_buffer_size,
240-
FALSE,
254+
(flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
241255
FILE_NOTIFY_CHANGE_FILE_NAME |
242256
FILE_NOTIFY_CHANGE_DIR_NAME |
243257
FILE_NOTIFY_CHANGE_ATTRIBUTES |
@@ -410,7 +424,9 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
410424

411425
if (long_filenamew) {
412426
/* Get the file name out of the long path. */
413-
result = uv_split_path(long_filenamew, NULL, &filenamew);
427+
result = uv_relative_path(long_filenamew,
428+
handle->dirw,
429+
&filenamew);
414430
uv__free(long_filenamew);
415431

416432
if (result == 0) {

src/win/internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ extern UV_THREAD_LOCAL int uv__crt_assert_enabled;
6464

6565
/* Used by all handles. */
6666
#define UV_HANDLE_CLOSED 0x00000002
67-
#define UV_HANDLE_ENDGAME_QUEUED 0x00000004
67+
#define UV_HANDLE_ENDGAME_QUEUED 0x00000008
6868

6969
/* uv-common.h: #define UV__HANDLE_CLOSING 0x00000001 */
7070
/* uv-common.h: #define UV__HANDLE_ACTIVE 0x00000040 */

test/test-fs-event.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
static uv_fs_event_t fs_event;
3939
static const char file_prefix[] = "fsevent-";
40+
static const char file_prefix_in_subdir[] = "subdir";
4041
static uv_timer_t timer;
4142
static int timer_cb_called;
4243
static int close_cb_called;
@@ -51,6 +52,7 @@ static char fs_event_filename[1024];
5152
static int timer_cb_touch_called;
5253

5354
static void fs_event_unlink_files(uv_timer_t* handle);
55+
static void fs_event_unlink_files_in_subdir(uv_timer_t* handle);
5456

5557
static void create_dir(uv_loop_t* loop, const char* name) {
5658
int r;
@@ -138,6 +140,25 @@ static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle,
138140
}
139141
}
140142

143+
static void fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t* handle,
144+
const char* filename,
145+
int events,
146+
int status) {
147+
fs_event_cb_called++;
148+
ASSERT(handle == &fs_event);
149+
ASSERT(status == 0);
150+
ASSERT(events == UV_RENAME || events == UV_CHANGE);
151+
ASSERT(filename == NULL ||
152+
strncmp(filename, file_prefix_in_subdir, sizeof(file_prefix_in_subdir) - 1) == 0);
153+
154+
/* Stop watching dir when received events about all files:
155+
* both create and close events */
156+
if (fs_event_cb_called == 2 * fs_event_file_count) {
157+
ASSERT(0 == uv_fs_event_stop(handle));
158+
uv_close((uv_handle_t*) handle, close_cb);
159+
}
160+
}
161+
141162
static const char* fs_event_get_filename(int i) {
142163
snprintf(fs_event_filename,
143164
sizeof(fs_event_filename),
@@ -147,6 +168,15 @@ static const char* fs_event_get_filename(int i) {
147168
return fs_event_filename;
148169
}
149170

171+
static const char* fs_event_get_filename_in_subdir(int i) {
172+
snprintf(fs_event_filename,
173+
sizeof(fs_event_filename),
174+
"watch_dir/subdir/%s%d",
175+
file_prefix,
176+
i);
177+
return fs_event_filename;
178+
}
179+
150180
static void fs_event_create_files(uv_timer_t* handle) {
151181
int i;
152182

@@ -164,6 +194,41 @@ static void fs_event_create_files(uv_timer_t* handle) {
164194
ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 50, 0));
165195
}
166196

197+
static void fs_event_create_files_in_subdir(uv_timer_t* handle) {
198+
int i;
199+
200+
/* Already created all files */
201+
if (fs_event_created == fs_event_file_count) {
202+
uv_close((uv_handle_t*) &timer, close_cb);
203+
return;
204+
}
205+
206+
/* Create all files */
207+
for (i = 0; i < 16; i++, fs_event_created++)
208+
create_file(handle->loop, fs_event_get_filename_in_subdir(i));
209+
210+
/* And unlink them */
211+
ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 50, 0));
212+
}
213+
214+
void fs_event_unlink_files_in_subdir(uv_timer_t* handle) {
215+
int r;
216+
int i;
217+
218+
/* NOTE: handle might be NULL if invoked not as timer callback */
219+
220+
/* Unlink all files */
221+
for (i = 0; i < 16; i++) {
222+
r = remove(fs_event_get_filename_in_subdir(i));
223+
if (handle != NULL)
224+
ASSERT(r == 0);
225+
}
226+
227+
/* And create them again */
228+
if (handle != NULL)
229+
ASSERT(0 == uv_timer_start(&timer, fs_event_create_files_in_subdir, 50, 0));
230+
}
231+
167232
void fs_event_unlink_files(uv_timer_t* handle) {
168233
int r;
169234
int i;
@@ -282,6 +347,51 @@ TEST_IMPL(fs_event_watch_dir) {
282347
return 0;
283348
}
284349

350+
TEST_IMPL(fs_event_watch_dir_recursive) {
351+
#if defined(__APPLE__) || defined(_WIN32)
352+
uv_loop_t* loop;
353+
int r;
354+
355+
/* Setup */
356+
loop = uv_default_loop();
357+
fs_event_unlink_files(NULL);
358+
remove("watch_dir/file2");
359+
remove("watch_dir/file1");
360+
remove("watch_dir/subdir");
361+
remove("watch_dir/");
362+
create_dir(loop, "watch_dir");
363+
create_dir(loop, "watch_dir/subdir");
364+
365+
r = uv_fs_event_init(loop, &fs_event);
366+
ASSERT(r == 0);
367+
r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file_in_subdir, "watch_dir", UV_FS_EVENT_RECURSIVE);
368+
ASSERT(r == 0);
369+
r = uv_timer_init(loop, &timer);
370+
ASSERT(r == 0);
371+
r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0);
372+
ASSERT(r == 0);
373+
374+
uv_run(loop, UV_RUN_DEFAULT);
375+
376+
ASSERT(fs_event_cb_called == 2 * fs_event_file_count);
377+
ASSERT(fs_event_created == fs_event_file_count);
378+
ASSERT(close_cb_called == 2);
379+
380+
/* Cleanup */
381+
fs_event_unlink_files_in_subdir(NULL);
382+
remove("watch_dir/file2");
383+
remove("watch_dir/file1");
384+
remove("watch_dir/subdir");
385+
remove("watch_dir/");
386+
387+
MAKE_VALGRIND_HAPPY();
388+
return 0;
389+
#else
390+
RETURN_SKIP("Recursive directory watching not supported on this platform.");
391+
#endif
392+
}
393+
394+
285395
TEST_IMPL(fs_event_watch_file) {
286396
uv_loop_t* loop = uv_default_loop();
287397
int r;

test/test-list.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ TEST_DECLARE (fs_file_open_append)
258258
TEST_DECLARE (fs_stat_missing_path)
259259
TEST_DECLARE (fs_read_file_eof)
260260
TEST_DECLARE (fs_event_watch_dir)
261+
TEST_DECLARE (fs_event_watch_dir_recursive)
261262
TEST_DECLARE (fs_event_watch_file)
262263
TEST_DECLARE (fs_event_watch_file_twice)
263264
TEST_DECLARE (fs_event_watch_file_current_dir)
@@ -664,6 +665,7 @@ TASK_LIST_START
664665
TEST_ENTRY (fs_read_file_eof)
665666
TEST_ENTRY (fs_file_open_append)
666667
TEST_ENTRY (fs_event_watch_dir)
668+
TEST_ENTRY (fs_event_watch_dir_recursive)
667669
TEST_ENTRY (fs_event_watch_file)
668670
TEST_ENTRY (fs_event_watch_file_twice)
669671
TEST_ENTRY (fs_event_watch_file_current_dir)

0 commit comments

Comments
 (0)