Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
unix, windows: add stat() based file watcher
Monitors a file path for changes. Supersedes ev_stat.
  • Loading branch information
bnoordhuis committed May 31, 2012
1 parent 9a3dff3 commit cc7c854
Show file tree
Hide file tree
Showing 15 changed files with 448 additions and 20 deletions.
4 changes: 2 additions & 2 deletions config-mingw.mk
Expand Up @@ -37,8 +37,8 @@ RUNNER_LINKFLAGS=$(LINKFLAGS)
RUNNER_LIBS=-lws2_32 -lpsapi -liphlpapi
RUNNER_SRC=test/runner-win.c

uv.a: $(WIN_OBJS) src/cares.o src/uv-common.o $(CARES_OBJS)
$(AR) rcs 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 $^

src/%.o: src/%.c include/uv.h include/uv-private/uv-win.h
$(CC) $(CFLAGS) -c $< -o $@
Expand Down
4 changes: 2 additions & 2 deletions config-unix.mk
Expand Up @@ -129,8 +129,8 @@ endif
RUNNER_LIBS=
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)
$(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)
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 $^

src/%.o: src/%.c include/uv.h include/uv-private/uv-unix.h
$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
Expand Down
3 changes: 3 additions & 0 deletions include/uv-private/uv-unix.h
Expand Up @@ -249,6 +249,9 @@ struct uv__io_s {
struct stat statbuf; \
eio_req* eio;

#define UV_FS_POLL_PRIVATE_FIELDS \
struct stat statbuf;

#define UV_WORK_PRIVATE_FIELDS \
eio_req* eio;

Expand Down
3 changes: 3 additions & 0 deletions include/uv-private/uv-win.h
Expand Up @@ -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_FS_EVENT_PRIVATE_FIELDS \
Expand Down
59 changes: 49 additions & 10 deletions include/uv.h
Expand Up @@ -140,6 +140,7 @@ typedef enum {
XX(ASYNC, async) \
XX(CHECK, check) \
XX(FS_EVENT, fs_event) \
XX(FS_POLL, fs_poll) \
XX(IDLE, idle) \
XX(NAMED_PIPE, pipe) \
XX(POLL, poll) \
Expand Down Expand Up @@ -209,6 +210,7 @@ typedef struct uv_udp_send_s uv_udp_send_t;
typedef struct uv_fs_s uv_fs_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_poll_s uv_fs_poll_t;
typedef struct uv_work_s uv_work_t;


Expand Down Expand Up @@ -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_check_cb)(uv_check_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,
struct addrinfo* res);
typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal);
Expand Down Expand Up @@ -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
* See: http://en.wikipedia.org/wiki/Load_(computing)
Expand Down Expand Up @@ -1683,22 +1721,23 @@ union uv_any_req {


struct uv_counters_s {
uint64_t async_init;
uint64_t check_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 stream_init;
uint64_t tcp_init;
uint64_t udp_init;
uint64_t idle_init;
uint64_t pipe_init;
uint64_t tty_init;
uint64_t poll_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 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;
};


Expand Down
221 changes: 221 additions & 0 deletions src/fs-poll.c
@@ -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 */
13 changes: 7 additions & 6 deletions src/unix/core.c
Expand Up @@ -109,6 +109,10 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
uv__poll_close((uv_poll_t*)handle);
break;

case UV_FS_POLL:
uv__fs_poll_close((uv_fs_poll_t*)handle);
break;

default:
assert(0);
}
Expand All @@ -133,6 +137,9 @@ static void uv__finish_close(uv_handle_t* handle) {
case UV_ASYNC:
case UV_TIMER:
case UV_PROCESS:
case UV_FS_EVENT:
case UV_FS_POLL:
case UV_POLL:
break;

case UV_NAMED_PIPE:
Expand All @@ -148,12 +155,6 @@ static void uv__finish_close(uv_handle_t* handle) {
uv__udp_finish_close((uv_udp_t*)handle);
break;

case UV_FS_EVENT:
break;

case UV_POLL:
break;

default:
assert(0);
break;
Expand Down

0 comments on commit cc7c854

Please sign in to comment.