unix, windows: add stat() based file watcher
Monitors a file path for changes. Supersedes ev_stat.
This commit is contained in:
parent
9a3dff35c0
commit
cc7c8542a5
@ -37,8 +37,8 @@ RUNNER_LINKFLAGS=$(LINKFLAGS)
|
|||||||
RUNNER_LIBS=-lws2_32 -lpsapi -liphlpapi
|
RUNNER_LIBS=-lws2_32 -lpsapi -liphlpapi
|
||||||
RUNNER_SRC=test/runner-win.c
|
RUNNER_SRC=test/runner-win.c
|
||||||
|
|
||||||
uv.a: $(WIN_OBJS) src/cares.o src/uv-common.o $(CARES_OBJS)
|
uv.a: $(WIN_OBJS) src/cares.o src/fs-poll.o src/uv-common.o $(CARES_OBJS)
|
||||||
$(AR) rcs uv.a $(WIN_OBJS) src/cares.o src/uv-common.o $(CARES_OBJS)
|
$(AR) rcs uv.a $^
|
||||||
|
|
||||||
src/%.o: src/%.c include/uv.h include/uv-private/uv-win.h
|
src/%.o: src/%.c include/uv.h include/uv-private/uv-win.h
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|||||||
@ -129,8 +129,8 @@ endif
|
|||||||
RUNNER_LIBS=
|
RUNNER_LIBS=
|
||||||
RUNNER_SRC=test/runner-unix.c
|
RUNNER_SRC=test/runner-unix.c
|
||||||
|
|
||||||
uv.a: $(OBJS) src/cares.o src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS)
|
uv.a: $(OBJS) src/cares.o src/fs-poll.o src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS)
|
||||||
$(AR) rcs uv.a $(OBJS) src/cares.o src/uv-common.o src/unix/uv-eio.o src/unix/ev/ev.o src/unix/eio/eio.o $(CARES_OBJS)
|
$(AR) rcs uv.a $^
|
||||||
|
|
||||||
src/%.o: src/%.c include/uv.h include/uv-private/uv-unix.h
|
src/%.o: src/%.c include/uv.h include/uv-private/uv-unix.h
|
||||||
$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||||
|
|||||||
@ -249,6 +249,9 @@ struct uv__io_s {
|
|||||||
struct stat statbuf; \
|
struct stat statbuf; \
|
||||||
eio_req* eio;
|
eio_req* eio;
|
||||||
|
|
||||||
|
#define UV_FS_POLL_PRIVATE_FIELDS \
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
#define UV_WORK_PRIVATE_FIELDS \
|
#define UV_WORK_PRIVATE_FIELDS \
|
||||||
eio_req* eio;
|
eio_req* eio;
|
||||||
|
|
||||||
|
|||||||
@ -487,6 +487,9 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
|
|||||||
}; \
|
}; \
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define UV_FS_POLL_PRIVATE_FIELDS \
|
||||||
|
struct _stati64 statbuf;
|
||||||
|
|
||||||
#define UV_WORK_PRIVATE_FIELDS \
|
#define UV_WORK_PRIVATE_FIELDS \
|
||||||
|
|
||||||
#define UV_FS_EVENT_PRIVATE_FIELDS \
|
#define UV_FS_EVENT_PRIVATE_FIELDS \
|
||||||
|
|||||||
59
include/uv.h
59
include/uv.h
@ -140,6 +140,7 @@ typedef enum {
|
|||||||
XX(ASYNC, async) \
|
XX(ASYNC, async) \
|
||||||
XX(CHECK, check) \
|
XX(CHECK, check) \
|
||||||
XX(FS_EVENT, fs_event) \
|
XX(FS_EVENT, fs_event) \
|
||||||
|
XX(FS_POLL, fs_poll) \
|
||||||
XX(IDLE, idle) \
|
XX(IDLE, idle) \
|
||||||
XX(NAMED_PIPE, pipe) \
|
XX(NAMED_PIPE, pipe) \
|
||||||
XX(POLL, poll) \
|
XX(POLL, poll) \
|
||||||
@ -209,6 +210,7 @@ typedef struct uv_udp_send_s uv_udp_send_t;
|
|||||||
typedef struct uv_fs_s uv_fs_t;
|
typedef struct uv_fs_s uv_fs_t;
|
||||||
/* uv_fs_event_t is a subclass of uv_handle_t. */
|
/* uv_fs_event_t is a subclass of uv_handle_t. */
|
||||||
typedef struct uv_fs_event_s uv_fs_event_t;
|
typedef struct uv_fs_event_s uv_fs_event_t;
|
||||||
|
typedef struct uv_fs_poll_s uv_fs_poll_t;
|
||||||
typedef struct uv_work_s uv_work_t;
|
typedef struct uv_work_s uv_work_t;
|
||||||
|
|
||||||
|
|
||||||
@ -295,6 +297,7 @@ typedef void (*uv_async_cb)(uv_async_t* handle, int status);
|
|||||||
typedef void (*uv_prepare_cb)(uv_prepare_t* handle, int status);
|
typedef void (*uv_prepare_cb)(uv_prepare_t* handle, int status);
|
||||||
typedef void (*uv_check_cb)(uv_check_t* handle, int status);
|
typedef void (*uv_check_cb)(uv_check_t* handle, int status);
|
||||||
typedef void (*uv_idle_cb)(uv_idle_t* handle, int status);
|
typedef void (*uv_idle_cb)(uv_idle_t* handle, int status);
|
||||||
|
typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, int status);
|
||||||
typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status,
|
typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status,
|
||||||
struct addrinfo* res);
|
struct addrinfo* res);
|
||||||
typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal);
|
typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal);
|
||||||
@ -1510,6 +1513,41 @@ struct uv_fs_event_s {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* uv_fs_stat() based polling file watcher.
|
||||||
|
*/
|
||||||
|
struct uv_fs_poll_s {
|
||||||
|
UV_HANDLE_FIELDS
|
||||||
|
/* Private, don't touch. */
|
||||||
|
int busy_polling; /* TODO(bnoordhuis) Fold into flags field. */
|
||||||
|
unsigned int interval;
|
||||||
|
uint64_t start_time;
|
||||||
|
char* path;
|
||||||
|
uv_fs_poll_cb poll_cb;
|
||||||
|
uv_timer_t timer_handle;
|
||||||
|
uv_fs_t* fs_req;
|
||||||
|
UV_FS_POLL_PRIVATE_FIELDS
|
||||||
|
};
|
||||||
|
|
||||||
|
UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the file at `path` for changes every `interval` milliseconds.
|
||||||
|
*
|
||||||
|
* Your callback gets invoked repeatedly with `status == -1` if `path`
|
||||||
|
* does not exist or is inaccessible. The watcher is *not* stopped. This
|
||||||
|
* lets you monitor a path until the resource becomes available (again).
|
||||||
|
*
|
||||||
|
* For maximum portability, use multi-second intervals. Sub-second intervals
|
||||||
|
* will not detect all changes on many file systems.
|
||||||
|
*/
|
||||||
|
UV_EXTERN int uv_fs_poll_start(uv_fs_poll_t* handle,
|
||||||
|
uv_fs_poll_cb poll_cb,
|
||||||
|
const char* path,
|
||||||
|
unsigned int interval);
|
||||||
|
|
||||||
|
UV_EXTERN int uv_fs_poll_stop(uv_fs_poll_t* handle);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets load avg
|
* Gets load avg
|
||||||
* See: http://en.wikipedia.org/wiki/Load_(computing)
|
* See: http://en.wikipedia.org/wiki/Load_(computing)
|
||||||
@ -1683,22 +1721,23 @@ union uv_any_req {
|
|||||||
|
|
||||||
|
|
||||||
struct uv_counters_s {
|
struct uv_counters_s {
|
||||||
|
uint64_t async_init;
|
||||||
|
uint64_t check_init;
|
||||||
uint64_t eio_init;
|
uint64_t eio_init;
|
||||||
uint64_t req_init;
|
uint64_t fs_event_init;
|
||||||
|
uint64_t fs_poll_init;
|
||||||
uint64_t handle_init;
|
uint64_t handle_init;
|
||||||
uint64_t stream_init;
|
uint64_t idle_init;
|
||||||
uint64_t tcp_init;
|
|
||||||
uint64_t udp_init;
|
|
||||||
uint64_t pipe_init;
|
uint64_t pipe_init;
|
||||||
uint64_t tty_init;
|
|
||||||
uint64_t poll_init;
|
uint64_t poll_init;
|
||||||
uint64_t prepare_init;
|
uint64_t prepare_init;
|
||||||
uint64_t check_init;
|
|
||||||
uint64_t idle_init;
|
|
||||||
uint64_t async_init;
|
|
||||||
uint64_t timer_init;
|
|
||||||
uint64_t process_init;
|
uint64_t process_init;
|
||||||
uint64_t fs_event_init;
|
uint64_t req_init;
|
||||||
|
uint64_t stream_init;
|
||||||
|
uint64_t tcp_init;
|
||||||
|
uint64_t timer_init;
|
||||||
|
uint64_t tty_init;
|
||||||
|
uint64_t udp_init;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
221
src/fs-poll.c
Normal file
221
src/fs-poll.c
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/* 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 "uv-common.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
typedef struct _stati64 uv__statbuf_t;
|
||||||
|
#else
|
||||||
|
typedef struct stat uv__statbuf_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int statbuf_eq(const uv__statbuf_t* a, const uv__statbuf_t* b);
|
||||||
|
static void timer_cb(uv_timer_t* timer, int status);
|
||||||
|
static void poll_cb(uv_fs_t* req);
|
||||||
|
|
||||||
|
|
||||||
|
int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
|
||||||
|
/* TODO(bnoordhuis) Mark fs_req internal. */
|
||||||
|
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
|
||||||
|
loop->counters.fs_poll_init++;
|
||||||
|
|
||||||
|
if (uv_timer_init(loop, &handle->timer_handle))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
handle->timer_handle.flags |= UV__HANDLE_INTERNAL;
|
||||||
|
uv__handle_unref(&handle->timer_handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int uv_fs_poll_start(uv_fs_poll_t* handle,
|
||||||
|
uv_fs_poll_cb cb,
|
||||||
|
const char* path,
|
||||||
|
unsigned int interval) {
|
||||||
|
uv_fs_t* req;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (uv__is_active(handle))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = strlen(path) + 1;
|
||||||
|
req = malloc(sizeof(*req) + len);
|
||||||
|
|
||||||
|
if (req == NULL)
|
||||||
|
return uv__set_artificial_error(handle->loop, UV_ENOMEM);
|
||||||
|
|
||||||
|
req->data = handle;
|
||||||
|
handle->path = memcpy(req + 1, path, len);
|
||||||
|
handle->fs_req = req;
|
||||||
|
handle->poll_cb = cb;
|
||||||
|
handle->interval = interval ? interval : 1;
|
||||||
|
handle->start_time = uv_now(handle->loop);
|
||||||
|
handle->busy_polling = 0;
|
||||||
|
|
||||||
|
if (uv_fs_stat(handle->loop, handle->fs_req, handle->path, poll_cb))
|
||||||
|
abort();
|
||||||
|
|
||||||
|
uv__handle_start(handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int uv_fs_poll_stop(uv_fs_poll_t* handle) {
|
||||||
|
if (!uv__is_active(handle))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Don't free the fs req if it's active. Signal poll_cb that it needs to free
|
||||||
|
* the req by removing the handle backlink.
|
||||||
|
*
|
||||||
|
* TODO(bnoordhuis) Have uv-unix postpone the close callback until the req
|
||||||
|
* finishes so we don't need this pointer / lifecycle hackery. The callback
|
||||||
|
* always runs on the next tick now.
|
||||||
|
*/
|
||||||
|
if (handle->fs_req->data)
|
||||||
|
handle->fs_req->data = NULL;
|
||||||
|
else
|
||||||
|
free(handle->fs_req);
|
||||||
|
|
||||||
|
handle->fs_req = NULL;
|
||||||
|
handle->path = NULL;
|
||||||
|
|
||||||
|
uv_timer_stop(&handle->timer_handle);
|
||||||
|
uv__handle_stop(handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void uv__fs_poll_close(uv_fs_poll_t* handle) {
|
||||||
|
uv_fs_poll_stop(handle);
|
||||||
|
uv_close((uv_handle_t*)&handle->timer_handle, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void timer_cb(uv_timer_t* timer, int status) {
|
||||||
|
uv_fs_poll_t* handle;
|
||||||
|
|
||||||
|
handle = container_of(timer, uv_fs_poll_t, timer_handle);
|
||||||
|
handle->start_time = uv_now(handle->loop);
|
||||||
|
handle->fs_req->data = handle;
|
||||||
|
|
||||||
|
if (uv_fs_stat(handle->loop, handle->fs_req, handle->path, poll_cb))
|
||||||
|
abort();
|
||||||
|
|
||||||
|
assert(uv__is_active(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void poll_cb(uv_fs_t* req) {
|
||||||
|
uv__statbuf_t* statbuf;
|
||||||
|
uv_fs_poll_t* handle;
|
||||||
|
uint64_t interval;
|
||||||
|
|
||||||
|
handle = req->data;
|
||||||
|
|
||||||
|
if (handle == NULL) /* Handle has been stopped or closed. */
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
assert(req == handle->fs_req);
|
||||||
|
|
||||||
|
if (req->result != 0) {
|
||||||
|
/* TODO(bnoordhuis) Only signal the error the first time? What if the
|
||||||
|
* error reason changes?
|
||||||
|
*/
|
||||||
|
uv__set_artificial_error(handle->loop, req->errorno);
|
||||||
|
handle->poll_cb(handle, -1);
|
||||||
|
handle->busy_polling = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
statbuf = req->ptr;
|
||||||
|
|
||||||
|
if (handle->busy_polling == 0) {
|
||||||
|
handle->statbuf = *statbuf;
|
||||||
|
handle->busy_polling = 1;
|
||||||
|
}
|
||||||
|
else if (handle->busy_polling == -1) {
|
||||||
|
handle->statbuf = *statbuf;
|
||||||
|
handle->busy_polling = 1;
|
||||||
|
handle->poll_cb(handle, 0); /* Error went away. */
|
||||||
|
}
|
||||||
|
else if (!statbuf_eq(statbuf, &handle->statbuf)) {
|
||||||
|
handle->statbuf = *statbuf;
|
||||||
|
handle->poll_cb(handle, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
|
|
||||||
|
if (req->data == NULL) { /* Handle has been stopped or closed. */
|
||||||
|
free(req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
req->data = NULL; /* Tell uv_fs_poll_stop() it's safe to free the req. */
|
||||||
|
|
||||||
|
/* Reschedule timer, subtract the delay from doing the stat(). */
|
||||||
|
interval = handle->interval;
|
||||||
|
interval -= (uv_now(handle->loop) - handle->start_time) % interval;
|
||||||
|
|
||||||
|
if (uv_timer_start(&handle->timer_handle, timer_cb, interval, 0))
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int statbuf_eq(const uv__statbuf_t* a, const uv__statbuf_t* b) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return a->st_mtime == b->st_mtime
|
||||||
|
&& a->st_size == b->st_size
|
||||||
|
&& a->st_mode == b->st_mode;
|
||||||
|
#else
|
||||||
|
return a->st_ctime == b->st_ctime
|
||||||
|
&& a->st_mtime == b->st_mtime
|
||||||
|
&& a->st_size == b->st_size
|
||||||
|
&& a->st_mode == b->st_mode
|
||||||
|
&& a->st_uid == b->st_uid
|
||||||
|
&& a->st_gid == b->st_gid
|
||||||
|
&& a->st_ino == b->st_ino
|
||||||
|
&& a->st_dev == b->st_dev;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include "win/internal.h"
|
||||||
|
#include "win/handle-inl.h"
|
||||||
|
|
||||||
|
void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle) {
|
||||||
|
assert(handle->flags & UV_HANDLE_CLOSING);
|
||||||
|
assert(!(handle->flags & UV_HANDLE_CLOSED));
|
||||||
|
uv__handle_stop(handle);
|
||||||
|
uv__handle_close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _WIN32 */
|
||||||
@ -109,6 +109,10 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
|
|||||||
uv__poll_close((uv_poll_t*)handle);
|
uv__poll_close((uv_poll_t*)handle);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UV_FS_POLL:
|
||||||
|
uv__fs_poll_close((uv_fs_poll_t*)handle);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
@ -133,6 +137,9 @@ static void uv__finish_close(uv_handle_t* handle) {
|
|||||||
case UV_ASYNC:
|
case UV_ASYNC:
|
||||||
case UV_TIMER:
|
case UV_TIMER:
|
||||||
case UV_PROCESS:
|
case UV_PROCESS:
|
||||||
|
case UV_FS_EVENT:
|
||||||
|
case UV_FS_POLL:
|
||||||
|
case UV_POLL:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UV_NAMED_PIPE:
|
case UV_NAMED_PIPE:
|
||||||
@ -148,12 +155,6 @@ static void uv__finish_close(uv_handle_t* handle) {
|
|||||||
uv__udp_finish_close((uv_udp_t*)handle);
|
uv__udp_finish_close((uv_udp_t*)handle);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UV_FS_EVENT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UV_POLL:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -86,6 +86,8 @@ int uv__tcp_connect6(uv_connect_t* req,
|
|||||||
struct sockaddr_in6 address,
|
struct sockaddr_in6 address,
|
||||||
uv_connect_cb cb);
|
uv_connect_cb cb);
|
||||||
|
|
||||||
|
void uv__fs_poll_close(uv_fs_poll_t* handle);
|
||||||
|
|
||||||
|
|
||||||
UNUSED static int uv__has_active_reqs(const uv_loop_t* loop) {
|
UNUSED static int uv__has_active_reqs(const uv_loop_t* loop) {
|
||||||
return !ngx_queue_empty(&loop->active_reqs);
|
return !ngx_queue_empty(&loop->active_reqs);
|
||||||
|
|||||||
@ -131,6 +131,10 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) {
|
|||||||
uv_fs_event_endgame(loop, (uv_fs_event_t*) handle);
|
uv_fs_event_endgame(loop, (uv_fs_event_t*) handle);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UV_FS_POLL:
|
||||||
|
uv__fs_poll_endgame(loop, (uv_fs_poll_t*) handle);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -132,6 +132,12 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) {
|
|||||||
uv_fs_event_close(loop, (uv_fs_event_t*) handle);
|
uv_fs_event_close(loop, (uv_fs_event_t*) handle);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case UV_FS_POLL:
|
||||||
|
uv__fs_poll_close((uv_fs_poll_t*) handle);
|
||||||
|
uv__handle_start(handle);
|
||||||
|
uv_want_endgame(loop, handle);
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* Not supported */
|
/* Not supported */
|
||||||
abort();
|
abort();
|
||||||
|
|||||||
@ -268,6 +268,12 @@ void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle);
|
|||||||
void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle);
|
void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stat poller.
|
||||||
|
*/
|
||||||
|
void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Utilities.
|
* Utilities.
|
||||||
*/
|
*/
|
||||||
|
|||||||
127
test/test-fs-poll.c
Normal file
127
test/test-fs-poll.c
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/* 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 "task.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#define FIXTURE "testfile"
|
||||||
|
|
||||||
|
static void poll_cb(uv_fs_poll_t* handle, int status);
|
||||||
|
static void timer_cb(uv_timer_t* handle, int status);
|
||||||
|
static void close_cb(uv_handle_t* handle);
|
||||||
|
|
||||||
|
static uv_fs_poll_t poll_handle;
|
||||||
|
static uv_timer_t timer_handle;
|
||||||
|
static uv_loop_t* loop;
|
||||||
|
|
||||||
|
static int poll_cb_called;
|
||||||
|
static int timer_cb_called;
|
||||||
|
static int close_cb_called;
|
||||||
|
|
||||||
|
|
||||||
|
static void touch_file(const char* path) {
|
||||||
|
static int count;
|
||||||
|
FILE* fp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ASSERT((fp = fopen(FIXTURE, "w+")));
|
||||||
|
|
||||||
|
/* Need to change the file size because the poller may not pick up
|
||||||
|
* sub-second mtime changes.
|
||||||
|
*/
|
||||||
|
i = ++count;
|
||||||
|
|
||||||
|
while (i--)
|
||||||
|
fputc('*', fp);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void close_cb(uv_handle_t* handle) {
|
||||||
|
close_cb_called++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void timer_cb(uv_timer_t* handle, int status) {
|
||||||
|
touch_file(FIXTURE);
|
||||||
|
timer_cb_called++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void poll_cb(uv_fs_poll_t* handle, int status) {
|
||||||
|
ASSERT(handle == &poll_handle);
|
||||||
|
ASSERT(uv_is_active((uv_handle_t*)handle));
|
||||||
|
|
||||||
|
switch (poll_cb_called++) {
|
||||||
|
case 0:
|
||||||
|
ASSERT(status == -1);
|
||||||
|
ASSERT(uv_last_error(loop).code == UV_ENOENT);
|
||||||
|
touch_file(FIXTURE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
ASSERT(status == 0);
|
||||||
|
ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 20, 0));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
ASSERT(status == 0);
|
||||||
|
ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 200, 0));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
ASSERT(status == 0);
|
||||||
|
remove(FIXTURE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
ASSERT(status == -1);
|
||||||
|
ASSERT(uv_last_error(loop).code == UV_ENOENT);
|
||||||
|
uv_close((uv_handle_t*)handle, close_cb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_IMPL(fs_poll) {
|
||||||
|
loop = uv_default_loop();
|
||||||
|
|
||||||
|
remove(FIXTURE);
|
||||||
|
|
||||||
|
ASSERT(0 == uv_timer_init(loop, &timer_handle));
|
||||||
|
ASSERT(0 == uv_fs_poll_init(loop, &poll_handle));
|
||||||
|
ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb, FIXTURE, 100));
|
||||||
|
ASSERT(0 == uv_run(loop));
|
||||||
|
|
||||||
|
ASSERT(poll_cb_called == 5);
|
||||||
|
ASSERT(timer_cb_called == 2);
|
||||||
|
ASSERT(close_cb_called == 1);
|
||||||
|
uv_loop_delete(loop);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -97,6 +97,7 @@ TEST_DECLARE (unref_in_prepare_cb)
|
|||||||
TEST_DECLARE (timer_ref)
|
TEST_DECLARE (timer_ref)
|
||||||
TEST_DECLARE (timer_ref2)
|
TEST_DECLARE (timer_ref2)
|
||||||
TEST_DECLARE (fs_event_ref)
|
TEST_DECLARE (fs_event_ref)
|
||||||
|
TEST_DECLARE (fs_poll_ref)
|
||||||
TEST_DECLARE (tcp_ref)
|
TEST_DECLARE (tcp_ref)
|
||||||
TEST_DECLARE (tcp_ref2)
|
TEST_DECLARE (tcp_ref2)
|
||||||
TEST_DECLARE (tcp_ref3)
|
TEST_DECLARE (tcp_ref3)
|
||||||
@ -134,6 +135,7 @@ TEST_DECLARE (spawn_and_ping)
|
|||||||
TEST_DECLARE (spawn_setuid_fails)
|
TEST_DECLARE (spawn_setuid_fails)
|
||||||
TEST_DECLARE (spawn_setgid_fails)
|
TEST_DECLARE (spawn_setgid_fails)
|
||||||
TEST_DECLARE (spawn_stdout_to_file)
|
TEST_DECLARE (spawn_stdout_to_file)
|
||||||
|
TEST_DECLARE (fs_poll)
|
||||||
TEST_DECLARE (kill)
|
TEST_DECLARE (kill)
|
||||||
TEST_DECLARE (fs_file_noent)
|
TEST_DECLARE (fs_file_noent)
|
||||||
TEST_DECLARE (fs_file_nametoolong)
|
TEST_DECLARE (fs_file_nametoolong)
|
||||||
@ -293,6 +295,7 @@ TASK_LIST_START
|
|||||||
|
|
||||||
TEST_ENTRY (ref)
|
TEST_ENTRY (ref)
|
||||||
TEST_ENTRY (idle_ref)
|
TEST_ENTRY (idle_ref)
|
||||||
|
TEST_ENTRY (fs_poll_ref)
|
||||||
TEST_ENTRY (async_ref)
|
TEST_ENTRY (async_ref)
|
||||||
TEST_ENTRY (prepare_ref)
|
TEST_ENTRY (prepare_ref)
|
||||||
TEST_ENTRY (check_ref)
|
TEST_ENTRY (check_ref)
|
||||||
@ -360,6 +363,7 @@ TASK_LIST_START
|
|||||||
TEST_ENTRY (spawn_setuid_fails)
|
TEST_ENTRY (spawn_setuid_fails)
|
||||||
TEST_ENTRY (spawn_setgid_fails)
|
TEST_ENTRY (spawn_setgid_fails)
|
||||||
TEST_ENTRY (spawn_stdout_to_file)
|
TEST_ENTRY (spawn_stdout_to_file)
|
||||||
|
TEST_ENTRY (fs_poll)
|
||||||
TEST_ENTRY (kill)
|
TEST_ENTRY (kill)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows)
|
TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows)
|
||||||
|
|||||||
@ -168,6 +168,16 @@ TEST_IMPL(fs_event_ref) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_IMPL(fs_poll_ref) {
|
||||||
|
uv_fs_poll_t h;
|
||||||
|
uv_fs_poll_init(uv_default_loop(), &h);
|
||||||
|
uv_fs_poll_start(&h, NULL, ".", 999);
|
||||||
|
uv_unref((uv_handle_t*)&h);
|
||||||
|
uv_run(uv_default_loop());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_IMPL(tcp_ref) {
|
TEST_IMPL(tcp_ref) {
|
||||||
uv_tcp_t h;
|
uv_tcp_t h;
|
||||||
uv_tcp_init(uv_default_loop(), &h);
|
uv_tcp_init(uv_default_loop(), &h);
|
||||||
|
|||||||
2
uv.gyp
2
uv.gyp
@ -50,6 +50,7 @@
|
|||||||
'include/uv-private/ngx-queue.h',
|
'include/uv-private/ngx-queue.h',
|
||||||
'include/uv-private/tree.h',
|
'include/uv-private/tree.h',
|
||||||
'src/cares.c',
|
'src/cares.c',
|
||||||
|
'src/fs-poll.c',
|
||||||
'src/uv-common.c',
|
'src/uv-common.c',
|
||||||
'src/uv-common.h',
|
'src/uv-common.h',
|
||||||
'src/ares/ares_cancel.c',
|
'src/ares/ares_cancel.c',
|
||||||
@ -339,6 +340,7 @@
|
|||||||
'test/test-shutdown-close.c',
|
'test/test-shutdown-close.c',
|
||||||
'test/test-shutdown-eof.c',
|
'test/test-shutdown-eof.c',
|
||||||
'test/test-spawn.c',
|
'test/test-spawn.c',
|
||||||
|
'test/test-fs-poll.c',
|
||||||
'test/test-stdio-over-pipes.c',
|
'test/test-stdio-over-pipes.c',
|
||||||
'test/test-tcp-bind-error.c',
|
'test/test-tcp-bind-error.c',
|
||||||
'test/test-tcp-bind6-error.c',
|
'test/test-tcp-bind6-error.c',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user