filesystem-notify-evt: change inotify() implementation
Create a single inotify() connection per place, which should reduce the latency of operations on filesystem change events and make them generally scale better on Linux. Internally, add a filesystem-never-changes mode, which could be useful for platforms with fixed filesystems, but it's also for experiments in checking the cost of filesystem change events.
This commit is contained in:
parent
d695e8a15a
commit
b3715ba36e
|
@ -355,6 +355,7 @@ typedef struct Thread_Local_Variables {
|
|||
Scheme_On_Atomic_Timeout_Proc on_atomic_timeout_;
|
||||
int atomic_timeout_auto_suspend_;
|
||||
int atomic_timeout_atomic_level_;
|
||||
void *scheme_inotify_server_;
|
||||
} Thread_Local_Variables;
|
||||
|
||||
#if defined(IMPLEMENT_THREAD_LOCAL_VIA_PTHREADS)
|
||||
|
@ -735,6 +736,7 @@ XFORM_GC_VARIABLE_STACK_THROUGH_THREAD_LOCAL;
|
|||
#define on_atomic_timeout XOA (scheme_get_thread_local_variables()->on_atomic_timeout_)
|
||||
#define atomic_timeout_auto_suspend XOA (scheme_get_thread_local_variables()->atomic_timeout_auto_suspend_)
|
||||
#define atomic_timeout_atomic_level XOA (scheme_get_thread_local_variables()->atomic_timeout_atomic_level_)
|
||||
#define scheme_inotify_server XOA (scheme_get_thread_local_variables()->scheme_inotify_server_)
|
||||
|
||||
/* **************************************** */
|
||||
|
||||
|
|
|
@ -383,7 +383,8 @@ optimize.@LTO@: $(COMMON_HEADERS) \
|
|||
place.@LTO@: $(COMMON_HEADERS) \
|
||||
$(srcdir)/stypes.h $(srcdir)/schfd.h $(srcdir)/mzmark_place.inc
|
||||
port.@LTO@: $(COMMON_HEADERS) \
|
||||
$(srcdir)/stypes.h $(srcdir)/schfd.h $(srcdir)/mzmark_port.inc
|
||||
$(srcdir)/stypes.h $(srcdir)/schfd.h $(srcdir)/mzmark_port.inc \
|
||||
$(srcdir)/inotify.inc
|
||||
portfun.@LTO@: $(COMMON_HEADERS) $(srcdir)/schvers.h \
|
||||
$(srcdir)/stypes.h $(srcdir)/schfd.h $(srcdir)/mzmark_portfun.inc
|
||||
print.@LTO@: $(COMMON_HEADERS) $(srcdir)/stypes.h $(srcdir)/schcpt.h \
|
||||
|
|
|
@ -624,6 +624,7 @@ void scheme_place_instance_destroy(int force)
|
|||
scheme_free_all_code();
|
||||
scheme_free_ghbn_data();
|
||||
scheme_release_kqueue();
|
||||
scheme_release_inotify();
|
||||
}
|
||||
|
||||
static void make_kernel_env(void)
|
||||
|
|
250
racket/src/racket/src/inotify.inc
Normal file
250
racket/src/racket/src/inotify.inc
Normal file
|
@ -0,0 +1,250 @@
|
|||
/* #included by "port.c" */
|
||||
|
||||
/* Multiplex multiple filesystem change events onto a single
|
||||
inotify connection. That's almost as easy as using watch
|
||||
descriptors in place of file descriptors, but using the
|
||||
same filesystem path multiple times produces the same
|
||||
watch descriptors, so reference-count it. Also, each watch
|
||||
can be removed as soon as it fires, since filesystem
|
||||
change events are single-shot.
|
||||
|
||||
The values returned by mz_inotify_add() are indices into an array
|
||||
of watch descriptors. There's room for a better data structure if
|
||||
the watch-descriptor-to-index mapping becomes too slow. */
|
||||
|
||||
#ifdef MZ_XFORM
|
||||
START_XFORM_SUSPEND;
|
||||
#endif
|
||||
|
||||
typedef struct mz_wd_t {
|
||||
int wd;
|
||||
int refcount;
|
||||
int val;
|
||||
} mz_wd_t;
|
||||
|
||||
typedef struct mz_inotify_state_t {
|
||||
int ready, fd;
|
||||
mz_wd_t *wds;
|
||||
int size, count;
|
||||
int got;
|
||||
} mz_inotify_state_t;
|
||||
|
||||
static int mzi_find_wd(int wd, mz_wd_t *wds, int size)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (wds[i].wd == wd) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mzi_add_wd(int wd, mz_wd_t *wds, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (wds[i].wd == wd) {
|
||||
wds[i].refcount++;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (!wds[i].refcount) {
|
||||
wds[i].wd = wd;
|
||||
wds[i].refcount = 1;
|
||||
wds[i].val = 0;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
abort();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mzi_pull_events(int fd, mz_wd_t *wds, int size)
|
||||
{
|
||||
struct inotify_event _ev, *ev;
|
||||
void *b = NULL;
|
||||
int rc, p, got = 0;
|
||||
int bsize;
|
||||
struct pollfd pfd[1];
|
||||
|
||||
ev = &_ev;
|
||||
bsize = sizeof(_ev);
|
||||
|
||||
pfd[0].fd = fd;
|
||||
pfd[0].events = POLLIN;
|
||||
|
||||
while (poll(pfd, 1, 0)) {
|
||||
rc = read(fd, ev, bsize);
|
||||
if (rc > 0) {
|
||||
p = mzi_find_wd(ev->wd, wds, size);
|
||||
if (p != -1) {
|
||||
got = 1;
|
||||
wds[p].val = 1;
|
||||
wds[p].wd = -1;
|
||||
inotify_rm_watch(fd, ev->wd);
|
||||
}
|
||||
} else if (rc == -1) {
|
||||
if (errno == EAGAIN)
|
||||
break;
|
||||
else if (errno == EINTR) {
|
||||
/* try again */
|
||||
} else if (errno == EINVAL) {
|
||||
bsize *= 2;
|
||||
if (b) free(b);
|
||||
b = malloc(bsize);
|
||||
ev = (struct inotify_event *)b;
|
||||
} else
|
||||
scheme_signal_error("inotify read failed on %d (%e)", fd, errno);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (b)
|
||||
free (b);
|
||||
|
||||
return got;
|
||||
}
|
||||
|
||||
static void mz_inotify_start(mz_inotify_state_t *s)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = inotify_init();
|
||||
|
||||
s->ready = 1;
|
||||
s->fd = fd;
|
||||
}
|
||||
|
||||
static void mz_inotify_end(mz_inotify_state_t *s)
|
||||
{
|
||||
int rc;
|
||||
|
||||
do {
|
||||
rc = close(s->fd);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
|
||||
if (s->wds) free(s->wds);
|
||||
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void mz_inotify_init()
|
||||
{
|
||||
if (!scheme_inotify_server) {
|
||||
mz_inotify_state_t *s;
|
||||
|
||||
s = (mz_inotify_state_t *)malloc(sizeof(mz_inotify_state_t));
|
||||
memset(s, 0, sizeof(mz_inotify_state_t));
|
||||
|
||||
mz_inotify_start(s);
|
||||
|
||||
scheme_inotify_server = s;
|
||||
}
|
||||
}
|
||||
|
||||
static int mz_inotify_ready()
|
||||
{
|
||||
mz_inotify_state_t *s = (mz_inotify_state_t *)scheme_inotify_server;
|
||||
|
||||
return s->ready;
|
||||
}
|
||||
|
||||
/* Other functions are called only if mz_inotify_ready() returns 1. */
|
||||
|
||||
static int mz_inotify_add(char *filename)
|
||||
{
|
||||
mz_inotify_state_t *s = (mz_inotify_state_t *)scheme_inotify_server;
|
||||
int wd;
|
||||
|
||||
if (s->count == s->size) {
|
||||
int new_size = (s->size ? (2 * s->size) : 32);
|
||||
mz_wd_t *new_wds;
|
||||
int i;
|
||||
new_wds = (mz_wd_t *)malloc(sizeof(mz_wd_t) * new_size);
|
||||
memcpy(new_wds, s->wds, s->size * sizeof(mz_wd_t));
|
||||
free(s->wds);
|
||||
s->wds = new_wds;
|
||||
s->size = new_size;
|
||||
for (i = s->count; i < s->size; i++)
|
||||
s->wds[i].wd = -1;
|
||||
}
|
||||
|
||||
wd = inotify_add_watch(s->fd, filename,
|
||||
(IN_CREATE | IN_DELETE | IN_DELETE_SELF
|
||||
| IN_MODIFY | IN_MOVE_SELF | IN_MOVED_TO
|
||||
| IN_ATTRIB | IN_ONESHOT));
|
||||
|
||||
if (wd == -1)
|
||||
return -1;
|
||||
else {
|
||||
int p;
|
||||
|
||||
p = mzi_add_wd(wd, s->wds, s->size);
|
||||
if (s->wds[p].refcount == 1)
|
||||
s->count++;
|
||||
|
||||
return p+1;
|
||||
}
|
||||
}
|
||||
|
||||
static void mz_inotify_remove(int p2)
|
||||
{
|
||||
mz_inotify_state_t *s = (mz_inotify_state_t *)scheme_inotify_server;
|
||||
int p = p2 - 1;
|
||||
|
||||
if (s->wds[p].refcount == 1) {
|
||||
if (s->wds[p].wd != -1) {
|
||||
inotify_rm_watch(s->fd, s->wds[p].wd);
|
||||
s->wds[p].wd = -1;
|
||||
/* in case the wd gets reused: */
|
||||
if (mzi_pull_events(s->fd, s->wds, s->size))
|
||||
s->got = 1;
|
||||
}
|
||||
--s->count;
|
||||
}
|
||||
s->wds[p].refcount -= 1;
|
||||
}
|
||||
|
||||
static int mz_inotify_poll(int p2)
|
||||
{
|
||||
mz_inotify_state_t *s = (mz_inotify_state_t *)scheme_inotify_server;
|
||||
int p = p2 - 1;
|
||||
|
||||
if (mzi_pull_events(s->fd, s->wds, s->size))
|
||||
s->got = 1;
|
||||
if (s->wds[p].val)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mz_inotify_stop()
|
||||
{
|
||||
mz_inotify_state_t *s = (mz_inotify_state_t *)scheme_inotify_server;
|
||||
|
||||
if (s) {
|
||||
mz_inotify_end(s);
|
||||
scheme_inotify_server = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mz_inotify_fd()
|
||||
{
|
||||
mz_inotify_state_t *s = (mz_inotify_state_t *)scheme_inotify_server;
|
||||
|
||||
if (s->got) {
|
||||
/* In case we received something for Y in a poll for X */
|
||||
s->got = 0;
|
||||
return -2;
|
||||
}
|
||||
|
||||
return s->fd;
|
||||
}
|
||||
|
||||
#ifdef MZ_XFORM
|
||||
END_XFORM_SUSPEND;
|
||||
#endif
|
|
@ -448,7 +448,7 @@ static int progress_evt_ready(Scheme_Object *rww, Scheme_Schedule_Info *sinfo);
|
|||
static int closed_evt_ready(Scheme_Object *rww, Scheme_Schedule_Info *sinfo);
|
||||
static int filesystem_change_evt_ready(Scheme_Object *evt, Scheme_Schedule_Info *sinfo);
|
||||
|
||||
#ifdef DOS_FILE_SYSTEM
|
||||
#if defined(DOS_FILE_SYSTEM) || defined(HAVE_INOTIFY_SYSCALL)
|
||||
static void filesystem_change_evt_need_wakeup (Scheme_Object *port, void *fds);
|
||||
#else
|
||||
# define filesystem_change_evt_need_wakeup NULL
|
||||
|
@ -5976,14 +5976,20 @@ Scheme_Object *scheme_file_unlock(int argc, Scheme_Object **argv)
|
|||
/* filesystem change events */
|
||||
/*========================================================================*/
|
||||
|
||||
/* removed `defined(HAVE_INOTIFY_SYSCALL)' for now: */
|
||||
#if defined(HAVE_KQUEUE_SYSCALL) || defined(DOS_FILE_SYSTEM)
|
||||
#if defined(HAVE_KQUEUE_SYSCALL) \
|
||||
|| defined(DOS_FILE_SYSTEM) \
|
||||
|| defined(HAVE_INOTIFY_SYSCALL) \
|
||||
|| defined(FILESYSTEM_NEVER_CHANGES)
|
||||
# define HAVE_FILESYSTEM_CHANGE_EVTS
|
||||
#else
|
||||
# define NO_FILESYSTEM_CHANGE_EVTS
|
||||
#endif
|
||||
|
||||
#ifndef NO_FILESYSTEM_CHANGE_EVTS
|
||||
#if defined(HAVE_INOTIFY_SYSCALL)
|
||||
# include "inotify.inc"
|
||||
#endif
|
||||
|
||||
#if !defined(NO_FILESYSTEM_CHANGE_EVTS) && !defined(FILESYSTEM_NEVER_CHANGES)
|
||||
static void filesystem_change_evt_fnl(void *fc, void *data)
|
||||
{
|
||||
scheme_filesystem_change_evt_cancel((Scheme_Object *)fc, NULL);
|
||||
|
@ -6005,6 +6011,8 @@ Scheme_Object *scheme_filesystem_change_evt(Scheme_Object *path, int flags, int
|
|||
#if defined(NO_FILESYSTEM_CHANGE_EVTS)
|
||||
ok = 0;
|
||||
errid = -1;
|
||||
#elif defined(FILESYSTEM_NEVER_CHANGES)
|
||||
ok = 1;
|
||||
#elif defined(HAVE_KQUEUE_SYSCALL)
|
||||
do {
|
||||
fd = open(filename, flags | MZ_BINARY, 0666);
|
||||
|
@ -6014,26 +6022,16 @@ Scheme_Object *scheme_filesystem_change_evt(Scheme_Object *path, int flags, int
|
|||
else
|
||||
ok = 1;
|
||||
#elif defined(HAVE_INOTIFY_SYSCALL)
|
||||
/* This implementation uses a file descriptor for every event,
|
||||
instead of using a watch descriptor for every event. This could
|
||||
be improved, but note that the kqueue() implementation needs a
|
||||
file descriptor per event, anyway. */
|
||||
fd = inotify_init();
|
||||
if (fd == -1)
|
||||
errid = errno;
|
||||
/* see "inotify.inc" */
|
||||
mz_inotify_init();
|
||||
if (!mz_inotify_ready())
|
||||
errid = EAGAIN;
|
||||
else {
|
||||
int wd;
|
||||
wd = inotify_add_watch(fd, filename,
|
||||
(IN_CREATE | IN_DELETE | IN_DELETE_SELF
|
||||
| IN_MODIFY | IN_MOVE_SELF | IN_MOVED_TO
|
||||
| IN_ATTRIB | IN_ONESHOT));
|
||||
if (wd == -1) {
|
||||
fd = mz_inotify_add(filename);
|
||||
if (fd == -1)
|
||||
errid = errno;
|
||||
scheme_close_file_fd(fd);
|
||||
} else {
|
||||
else
|
||||
ok = 1;
|
||||
fcntl(fd, F_SETFL, MZ_NONBLOCKING);
|
||||
}
|
||||
}
|
||||
#elif defined(DOS_FILE_SYSTEM)
|
||||
{
|
||||
|
@ -6091,7 +6089,16 @@ Scheme_Object *scheme_filesystem_change_evt(Scheme_Object *path, int flags, int
|
|||
|
||||
#if defined(NO_FILESYSTEM_CHANGE_EVTS)
|
||||
return NULL;
|
||||
#elif defined(DOS_FILE_SYSTEM)
|
||||
#elif defined(FILESYSTEM_NEVER_CHANGES)
|
||||
{
|
||||
Scheme_Filesystem_Change_Evt *fc;
|
||||
|
||||
fc = MALLOC_ONE_TAGGED(Scheme_Filesystem_Change_Evt);
|
||||
fc->so.type = scheme_filesystem_change_evt_type;
|
||||
|
||||
return (Scheme_Object *)fc;
|
||||
}
|
||||
#elif defined(DOS_FILE_SYSTEM) || defined(HAVE_INOTIFY_SYSCALL)
|
||||
{
|
||||
Scheme_Filesystem_Change_Evt *fc;
|
||||
Scheme_Custodian_Reference *mref;
|
||||
|
@ -6154,18 +6161,27 @@ void scheme_filesystem_change_evt_cancel(Scheme_Object *evt, void *ignored_data)
|
|||
Scheme_Filesystem_Change_Evt *fc = (Scheme_Filesystem_Change_Evt *)evt;
|
||||
|
||||
if (fc->mref) {
|
||||
# if defined(DOS_FILE_SYSTEM)
|
||||
# if defined(FILESYSTEM_NEVER_CHANGES)
|
||||
fc->mref = NULL;
|
||||
# else
|
||||
# if defined(DOS_FILE_SYSTEM)
|
||||
if (fc->fd) {
|
||||
FindCloseChangeNotification((HANDLE)fc->fd);
|
||||
fc->fd = 0;
|
||||
}
|
||||
# else
|
||||
# elif defined(HAVE_INOTIFY_SYSCALL)
|
||||
if (fc->fd) {
|
||||
mz_inotify_remove(fc->fd);
|
||||
fc->fd = 0;
|
||||
}
|
||||
# else
|
||||
(void)scheme_fd_to_semaphore(fc->fd, MZFD_REMOVE_VNODE, 0);
|
||||
scheme_close_file_fd(fc->fd);
|
||||
scheme_post_sema_all(fc->sema);
|
||||
# endif
|
||||
# endif
|
||||
scheme_remove_managed(fc->mref, (Scheme_Object *)fc);
|
||||
fc->mref = NULL;
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -6182,6 +6198,15 @@ static int filesystem_change_evt_ready(Scheme_Object *evt, Scheme_Schedule_Info
|
|||
}
|
||||
|
||||
return !fc->fd;
|
||||
# elif defined(HAVE_INOTIFY_SYSCALL)
|
||||
if (fc->fd) {
|
||||
if (mz_inotify_poll(fc->fd))
|
||||
scheme_filesystem_change_evt_cancel((Scheme_Object *)fc, NULL);
|
||||
}
|
||||
|
||||
return !fc->fd;
|
||||
# elif defined(FILESYSTEM_NEVER_CHANGES)
|
||||
return fc->fd; /* = 0 */
|
||||
# else
|
||||
if (scheme_try_plain_sema(fc->sema))
|
||||
scheme_filesystem_change_evt_cancel((Scheme_Object *)fc, NULL);
|
||||
|
@ -6195,13 +6220,28 @@ static int filesystem_change_evt_ready(Scheme_Object *evt, Scheme_Schedule_Info
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DOS_FILE_SYSTEM
|
||||
#if defined(DOS_FILE_SYSTEM) || defined(HAVE_INOTIFY_SYSCALL)
|
||||
static void filesystem_change_evt_need_wakeup (Scheme_Object *evt, void *fds)
|
||||
{
|
||||
Scheme_Filesystem_Change_Evt *fc = (Scheme_Filesystem_Change_Evt *)evt;
|
||||
|
||||
if (fc->fd)
|
||||
if (fc->fd) {
|
||||
#ifdef DOS_FILE_SYSTEM
|
||||
scheme_add_fd_handle((void *)fc->fd, fds, 0);
|
||||
#else
|
||||
int fd;
|
||||
fd = mz_inotify_fd();
|
||||
if (fd >= 0) {
|
||||
void *fds2;
|
||||
fds2 = MZ_GET_FDSET(fds, 0);
|
||||
MZ_FD_SET(fd, (fd_set *)fds2);
|
||||
fds2 = MZ_GET_FDSET(fds, 2);
|
||||
MZ_FD_SET(fd, (fd_set *)fds2);
|
||||
} else if (fd == -2) {
|
||||
scheme_cancel_sleep();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -6225,6 +6265,13 @@ int scheme_fd_regular_file(intptr_t fd, int dir_ok)
|
|||
#endif
|
||||
}
|
||||
|
||||
void scheme_release_inotify()
|
||||
{
|
||||
#ifdef HAVE_INOTIFY_SYSCALL
|
||||
mz_inotify_stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*========================================================================*/
|
||||
/* FILE input ports */
|
||||
/*========================================================================*/
|
||||
|
@ -6378,7 +6425,7 @@ static CSI_proc get_csi(void)
|
|||
static int tried_csi = 0;
|
||||
static CSI_proc csi;
|
||||
|
||||
START_XFORM_SKIP;
|
||||
START_XFORM_SKIP;
|
||||
if (!tried_csi) {
|
||||
HMODULE hm;
|
||||
hm = LoadLibrary("kernel32.dll");
|
||||
|
|
|
@ -3821,6 +3821,7 @@ void scheme_release_file_descriptor(void);
|
|||
|
||||
void scheme_init_kqueue(void);
|
||||
void scheme_release_kqueue(void);
|
||||
void scheme_release_inotify(void);
|
||||
|
||||
THREAD_LOCAL_DECL(extern struct mz_fd_set *scheme_semaphore_fd_set);
|
||||
THREAD_LOCAL_DECL(extern Scheme_Hash_Table *scheme_semaphore_fd_mapping);
|
||||
|
|
Loading…
Reference in New Issue
Block a user