unix, windows: add uv_fs_event_start/stop functions

Make uv_fs_event behave like other handles, that is, it's inactive after
init, and it's active between start and stop.
This commit is contained in:
Saúl Ibarra Corretgé 2013-09-24 00:08:31 +02:00
parent b0bb8deb17
commit 9d44d786ad
9 changed files with 261 additions and 139 deletions

View File

@ -534,9 +534,6 @@ UV_EXTERN size_t uv_req_size(uv_req_type type);
* function, then it's active from the moment that function is called.
* Likewise, uv_foo_stop() deactivates the handle again.
*
* - A uv_fs_event_t handle is currently always active. Future versions
* of libuv may add uv_fs_event_start() and uv_fs_event_stop() functions.
*
*/
UV_EXTERN int uv_is_active(const uv_handle_t* handle);
@ -1850,7 +1847,7 @@ UV_EXTERN void uv_loadavg(double avg[3]);
/*
* Flags to be passed to uv_fs_event_init.
* Flags to be passed to uv_fs_event_start.
*/
enum uv_fs_event_flags {
/*
@ -1880,8 +1877,15 @@ enum uv_fs_event_flags {
};
UV_EXTERN int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle,
const char* filename, uv_fs_event_cb cb, int flags);
UV_EXTERN int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle);
UV_EXTERN int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* filename,
unsigned int flags);
UV_EXTERN int uv_fs_event_stop(uv_fs_event_t* handle);
/* Utility */

View File

@ -128,12 +128,20 @@ void uv_loadavg(double avg[3]) {
}
int uv_fs_event_init(uv_loop_t* loop,
uv_fs_event_t* handle,
const char* filename,
uv_fs_event_cb cb,
int flags) {
loop->counters.fs_event_init++;
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
return -ENOSYS;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* filename,
unsigned int flags) {
return -ENOSYS;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
return -ENOSYS;
}

View File

@ -410,7 +410,7 @@ final:
*
* NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
*/
if (uv__is_closing(handle))
if (!uv__is_active(handle))
uv_sem_post(&state->fsevent_sem);
}

View File

@ -296,23 +296,30 @@ static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
}
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 uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
return 0;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* filename,
unsigned int flags) {
#if defined(__APPLE__)
struct stat statbuf;
#endif /* defined(__APPLE__) */
int fd;
if (uv__is_active(handle))
return -EINVAL;
/* TODO open asynchronously - but how do we report back errors? */
fd = open(filename, O_RDONLY);
if (fd == -1)
return -errno;
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
uv__handle_start(handle); /* FIXME shouldn't start automatically */
uv__handle_start(handle);
uv__io_init(&handle->event_watcher, uv__fs_event, fd);
handle->filename = strdup(filename);
handle->cb = cb;
@ -335,13 +342,18 @@ int uv_fs_event_init(uv_loop_t* loop,
fallback:
#endif /* defined(__APPLE__) */
uv__io_start(loop, &handle->event_watcher, UV__POLLIN);
uv__io_start(handle->loop, &handle->event_watcher, UV__POLLIN);
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
int uv_fs_event_stop(uv_fs_event_t* handle) {
if (!uv__is_active(handle))
return -EINVAL;
uv__handle_stop(handle);
#if defined(__APPLE__)
if (uv__fsevents_close(handle))
uv__io_stop(handle->loop, &handle->event_watcher, UV__POLLIN);
@ -349,11 +361,16 @@ void uv__fs_event_close(uv_fs_event_t* handle) {
uv__io_stop(handle->loop, &handle->event_watcher, UV__POLLIN);
#endif /* defined(__APPLE__) */
uv__handle_stop(handle);
free(handle->filename);
handle->filename = NULL;
uv__close(handle->event_watcher.fd);
handle->event_watcher.fd = -1;
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}

View File

@ -168,17 +168,25 @@ static void uv__inotify_read(uv_loop_t* loop,
}
int uv_fs_event_init(uv_loop_t* loop,
uv_fs_event_t* handle,
const char* path,
uv_fs_event_cb cb,
int flags) {
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
return 0;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* path,
unsigned int flags) {
struct watcher_list* w;
int events;
int err;
int wd;
err = init_inotify(loop);
if (uv__is_active(handle))
return -EINVAL;
err = init_inotify(handle->loop);
if (err)
return err;
@ -191,11 +199,11 @@ int uv_fs_event_init(uv_loop_t* loop,
| UV__IN_MOVED_FROM
| UV__IN_MOVED_TO;
wd = uv__inotify_add_watch(loop->inotify_fd, path, events);
wd = uv__inotify_add_watch(handle->loop->inotify_fd, path, events);
if (wd == -1)
return -errno;
w = find_watcher(loop, wd);
w = find_watcher(handle->loop, wd);
if (w)
goto no_insert;
@ -206,11 +214,10 @@ int uv_fs_event_init(uv_loop_t* loop,
w->wd = wd;
w->path = strcpy((char*)(w + 1), path);
QUEUE_INIT(&w->watchers);
RB_INSERT(watcher_root, CAST(&loop->inotify_watchers), w);
RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
no_insert:
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
uv__handle_start(handle); /* FIXME shouldn't start automatically */
uv__handle_start(handle);
QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers);
handle->filename = w->path;
handle->cb = cb;
@ -220,9 +227,12 @@ no_insert:
}
void uv__fs_event_close(uv_fs_event_t* handle) {
int uv_fs_event_stop(uv_fs_event_t* handle) {
struct watcher_list* w;
if (!uv__is_active(handle))
return -EINVAL;
w = find_watcher(handle->loop, handle->wd);
assert(w != NULL);
@ -237,4 +247,11 @@ void uv__fs_event_close(uv_fs_event_t* handle) {
uv__inotify_rm_watch(handle->loop->inotify_fd, w->wd);
free(w);
}
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}

View File

@ -352,24 +352,32 @@ static void uv__fs_event_read(uv_loop_t* loop,
}
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 portfd;
int first_run = 0;
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
return 0;
}
if (loop->fs_fd == -1) {
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* filename,
unsigned int flags) {
int portfd;
int first_run;
if (uv__is_active(handle))
return -EINVAL;
first_run = 0;
if (handle->loop->fs_fd == -1) {
portfd = port_create();
if (portfd == -1)
return -errno;
loop->fs_fd = portfd;
handle->loop->fs_fd = portfd;
first_run = 1;
}
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
uv__handle_start(handle); /* FIXME shouldn't start automatically */
uv__handle_start(handle);
handle->filename = strdup(filename);
handle->fd = PORT_UNUSED;
handle->cb = cb;
@ -379,34 +387,53 @@ int uv_fs_event_init(uv_loop_t* loop,
uv__fs_event_rearm(handle); /* FIXME(bnoordhuis) Check return code. */
if (first_run) {
uv__io_init(&loop->fs_event_watcher, uv__fs_event_read, portfd);
uv__io_start(loop, &loop->fs_event_watcher, UV__POLLIN);
uv__io_init(&handle->loop->fs_event_watcher, uv__fs_event_read, portfd);
uv__io_start(handle->loop, &handle->loop->fs_event_watcher, UV__POLLIN);
}
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
int uv_fs_event_stop(uv_fs_event_t* handle) {
if (!uv__is_active(handle))
return -EINVAL;
if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) {
port_dissociate(handle->loop->fs_fd,
PORT_SOURCE_FILE,
(uintptr_t) &handle->fo);
}
handle->fd = PORT_DELETED;
free(handle->filename);
handle->filename = NULL;
handle->fo.fo_name = NULL;
uv__handle_stop(handle);
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}
#else /* !defined(PORT_SOURCE_FILE) */
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 uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
return -ENOSYS;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* filename,
unsigned int flags) {
return -ENOSYS;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
return -ENOSYS;
}

View File

@ -34,30 +34,6 @@
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) {
uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);
handle->cb = cb;
handle->dir_handle = INVALID_HANDLE_VALUE;
handle->buffer = NULL;
handle->req_pending = 0;
handle->filew = NULL;
handle->short_filew = NULL;
handle->dirw = 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");
}
uv__handle_start(handle);
}
static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
uv_fs_event_t* handle) {
assert(handle->dir_handle != INVALID_HANDLE_VALUE);
@ -131,14 +107,42 @@ static int uv_split_path(const WCHAR* filename, WCHAR** dir,
}
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 uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);
handle->dir_handle = INVALID_HANDLE_VALUE;
handle->buffer = NULL;
handle->req_pending = 0;
handle->filew = NULL;
handle->short_filew = NULL;
handle->dirw = NULL;
uv_req_init(loop, (uv_req_t*)&handle->req);
handle->req.type = UV_FS_EVENT_REQ;
handle->req.data = handle;
return 0;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* filename,
unsigned int flags) {
int name_size, is_path_dir;
DWORD attr, last_error;
WCHAR* dir = NULL, *dir_to_watch, *filenamew = NULL;
WCHAR short_path[MAX_PATH];
uv_fs_event_init_handle(loop, handle, filename, cb);
if (uv__is_active(handle))
return UV_EINVAL;
handle->cb = cb;
handle->filename = strdup(filename);
if (!handle->filename) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
uv__handle_start(handle);
/* Convert name to UTF16. */
name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(WCHAR);
@ -213,15 +217,17 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle,
}
if (CreateIoCompletionPort(handle->dir_handle,
loop->iocp,
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) {
handle->buffer = (char*)_aligned_malloc(uv_directory_watcher_buffer_size,
sizeof(DWORD));
}
if (!handle->buffer) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
@ -282,6 +288,41 @@ error:
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
if (!uv__is_active(handle))
return UV_EINVAL;
if (handle->dir_handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle->dir_handle);
handle->dir_handle = INVALID_HANDLE_VALUE;
}
uv__handle_stop(handle);
if (handle->filew) {
free(handle->filew);
handle->filew = NULL;
}
if (handle->short_filew) {
free(handle->short_filew);
handle->short_filew = NULL;
}
if (handle->filename) {
free(handle->filename);
handle->filename = NULL;
}
if (handle->dirw) {
free(handle->dirw);
handle->dirw = NULL;
}
return 0;
}
void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
uv_fs_event_t* handle) {
FILE_NOTIFY_INFORMATION* file_info;
@ -294,12 +335,16 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
assert(handle->req_pending);
handle->req_pending = 0;
/* If we're closing, don't report any callbacks, and just push the handle */
/* onto the endgame queue. */
if (handle->flags & UV__HANDLE_CLOSING) {
uv_want_endgame(loop, (uv_handle_t*) handle);
/* Don't report any callbacks if:
* - We're closing, just push the handle onto the endgame queue
* - We are not active, just ignore the callback
*/
if (!uv__is_active(handle)) {
if (handle->flags & UV__HANDLE_CLOSING) {
uv_want_endgame(loop, (uv_handle_t*) handle);
}
return;
};
}
file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
@ -456,22 +501,19 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
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;
}
uv_fs_event_stop(handle);
uv__handle_closing(handle);
if (!handle->req_pending) {
uv_want_endgame(loop, (uv_handle_t*)handle);
}
uv__handle_closing(handle);
}
void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
if (handle->flags & UV__HANDLE_CLOSING &&
!handle->req_pending) {
if ((handle->flags & UV__HANDLE_CLOSING) && !handle->req_pending) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
if (handle->buffer) {
@ -479,26 +521,6 @@ void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
handle->buffer = NULL;
}
if (handle->filew) {
free(handle->filew);
handle->filew = NULL;
}
if (handle->short_filew) {
free(handle->short_filew);
handle->short_filew = NULL;
}
if (handle->filename) {
free(handle->filename);
handle->filename = NULL;
}
if (handle->dirw) {
free(handle->dirw);
handle->dirw = NULL;
}
uv__handle_close(handle);
}
}

View File

@ -103,6 +103,7 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename,
ASSERT(status == 0);
ASSERT(events == UV_RENAME);
ASSERT(filename == NULL || strcmp(filename, "file1") == 0);
ASSERT(0 == uv_fs_event_stop(handle));
uv_close((uv_handle_t*)handle, close_cb);
}
@ -113,6 +114,7 @@ static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename,
ASSERT(status == 0);
ASSERT(events == UV_CHANGE);
ASSERT(filename == NULL || strcmp(filename, "file2") == 0);
ASSERT(0 == uv_fs_event_stop(handle));
uv_close((uv_handle_t*)handle, close_cb);
}
@ -187,7 +189,9 @@ TEST_IMPL(fs_event_watch_dir) {
remove("watch_dir/");
create_dir(loop, "watch_dir");
r = uv_fs_event_init(loop, &fs_event, "watch_dir", fs_event_cb_dir, 0);
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);
ASSERT(r == 0);
r = uv_timer_init(loop, &timer);
ASSERT(r == 0);
@ -221,7 +225,9 @@ TEST_IMPL(fs_event_watch_file) {
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, 0);
r = uv_fs_event_init(loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file2", 0);
ASSERT(r == 0);
r = uv_timer_init(loop, &timer);
ASSERT(r == 0);
@ -252,8 +258,10 @@ TEST_IMPL(fs_event_watch_file_twice) {
loop = uv_default_loop();
timer.data = watchers;
ASSERT(0 == uv_fs_event_init(loop, watchers + 0, path, fail_cb, 0));
ASSERT(0 == uv_fs_event_init(loop, watchers + 1, path, fail_cb, 0));
ASSERT(0 == uv_fs_event_init(loop, watchers + 0));
ASSERT(0 == uv_fs_event_start(watchers + 0, fail_cb, path, 0));
ASSERT(0 == uv_fs_event_init(loop, watchers + 1));
ASSERT(0 == uv_fs_event_start(watchers + 1, fail_cb, path, 0));
ASSERT(0 == uv_timer_init(loop, &timer));
ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0));
ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
@ -273,9 +281,14 @@ TEST_IMPL(fs_event_watch_file_current_dir) {
remove("watch_file");
create_file(loop, "watch_file");
r = uv_fs_event_init(loop, &fs_event, "watch_file",
fs_event_cb_file_current_dir, 0);
r = uv_fs_event_init(loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event,
fs_event_cb_file_current_dir,
"watch_file",
0);
ASSERT(r == 0);
r = uv_timer_init(loop, &timer);
ASSERT(r == 0);
@ -310,12 +323,14 @@ TEST_IMPL(fs_event_no_callback_after_close) {
create_dir(loop, "watch_dir");
create_file(loop, "watch_dir/file1");
r = uv_fs_event_init(loop,
&fs_event,
"watch_dir/file1",
fs_event_cb_file,
0);
r = uv_fs_event_init(loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event,
fs_event_cb_file,
"watch_dir/file1",
0);
ASSERT(r == 0);
uv_close((uv_handle_t*)&fs_event, close_cb);
touch_file(loop, "watch_dir/file1");
@ -342,11 +357,12 @@ TEST_IMPL(fs_event_no_callback_on_close) {
create_dir(loop, "watch_dir");
create_file(loop, "watch_dir/file1");
r = uv_fs_event_init(loop,
&fs_event,
"watch_dir/file1",
fs_event_cb_file,
0);
r = uv_fs_event_init(loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event,
fs_event_cb_file,
"watch_dir/file1",
0);
ASSERT(r == 0);
uv_close((uv_handle_t*)&fs_event, close_cb);
@ -376,7 +392,9 @@ static void timer_cb(uv_timer_t* handle, int status) {
ASSERT(status == 0);
r = uv_fs_event_init(handle->loop, &fs_event, ".", fs_event_fail, 0);
r = uv_fs_event_init(handle->loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0);
ASSERT(r == 0);
uv_close((uv_handle_t*)&fs_event, close_cb);
@ -415,7 +433,9 @@ TEST_IMPL(fs_event_close_with_pending_event) {
create_dir(loop, "watch_dir");
create_file(loop, "watch_dir/file");
r = uv_fs_event_init(loop, &fs_event, "watch_dir", fs_event_fail, 0);
r = uv_fs_event_init(loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0);
ASSERT(r == 0);
/* Generate an fs event. */
@ -474,7 +494,9 @@ TEST_IMPL(fs_event_close_in_callback) {
create_file(loop, "watch_dir/file4");
create_file(loop, "watch_dir/file5");
r = uv_fs_event_init(loop, &fs_event, "watch_dir", fs_event_cb_close, 0);
r = uv_fs_event_init(loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0);
ASSERT(r == 0);
/* Generate a couple of fs events. */
@ -513,10 +535,14 @@ TEST_IMPL(fs_event_start_and_close) {
create_dir(loop, "watch_dir");
r = uv_fs_event_init(loop, &fs_event1, "watch_dir", fs_event_cb_dir, 0);
r = uv_fs_event_init(loop, &fs_event1);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0);
ASSERT(r == 0);
r = uv_fs_event_init(loop, &fs_event2, "watch_dir", fs_event_cb_dir, 0);
r = uv_fs_event_init(loop, &fs_event2);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0);
ASSERT(r == 0);
uv_close((uv_handle_t*) &fs_event2, close_cb);

View File

@ -196,7 +196,8 @@ TEST_IMPL(timer_ref2) {
TEST_IMPL(fs_event_ref) {
uv_fs_event_t h;
uv_fs_event_init(uv_default_loop(), &h, ".", (uv_fs_event_cb)fail_cb, 0);
uv_fs_event_init(uv_default_loop(), &h);
uv_fs_event_start(&h, (uv_fs_event_cb)fail_cb, ".", 0);
uv_unref((uv_handle_t*)&h);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
do_close(&h);