Bug 79605 - Update port: devel/gamin (fix and improve the kqueue backend)
Summary: Update port: devel/gamin (fix and improve the kqueue backend)
Status: Closed FIXED
Alias: None
Product: Ports & Packages
Classification: Unclassified
Component: Individual Port(s) (show other bugs)
Version: Latest
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-gnome (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-04-06 18:20 UTC by Jean-Yves Lefort
Modified: 2005-04-07 02:18 UTC (History)
1 user (show)

See Also:


Attachments
file.diff (11.91 KB, patch)
2005-04-06 18:20 UTC, Jean-Yves Lefort
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jean-Yves Lefort 2005-04-06 18:20:05 UTC
* When a file monitored with kqueue is moved or removed, gamin does
  not monitor it anymore. The patch fixes the issue by adding
  moved/removed files to the exist_list, so that gamin detects when
  they are recreated.
* No CHANGED event is emitted for the files contained in a monitored
  directory, because kqueue can't do that. The patch adds periodic
  polling for these files.
* Instead of calling kevent() every second, the patch uses an I/O
  watch (g_io_add_watch()).
Comment 1 Joe Marcus Clarke freebsd_committer freebsd_triage 2005-04-06 18:58:07 UTC
Responsible Changed
From-To: freebsd-ports-bugs->gnome

Over to maintainers.
Comment 2 Jean-Yves Lefort 2005-04-06 19:53:02 UTC
Update: use lstat() instead of stat(), and use more fields of the stat
structure for determining if a file has changed.

diff -ruN /usr/ports/devel/gamin/Makefile gamin/Makefile
--- /usr/ports/devel/gamin/Makefile	Sat Apr  2 11:08:46 2005
+++ gamin/Makefile	Wed Apr  6 18:57:30 2005
@@ -7,7 +7,7 @@
 
 PORTNAME=	gamin
 PORTVERSION=	0.0.26
-PORTREVISION?=	8
+PORTREVISION?=	9
 CATEGORIES?=	devel
 MASTER_SITES=	http://www.gnome.org/~veillard/gamin/sources/
 
diff -ruN /usr/ports/devel/gamin/files/patch-server_gam_kqueue.c gamin/files/patch-server_gam_kqueue.c
--- /usr/ports/devel/gamin/files/patch-server_gam_kqueue.c	Sat Apr  2 11:08:47 2005
+++ gamin/files/patch-server_gam_kqueue.c	Wed Apr  6 20:40:49 2005
@@ -1,8 +1,9 @@
---- server/gam_kqueue.c.orig	Thu Mar 31 20:39:54 2005
-+++ server/gam_kqueue.c	Fri Apr  1 01:09:11 2005
-@@ -0,0 +1,636 @@
+--- server/gam_kqueue.c.orig	Wed Apr  6 19:35:51 2005
++++ server/gam_kqueue.c	Wed Apr  6 20:35:47 2005
+@@ -0,0 +1,721 @@
 +/*
 + * Copyright (C) 2005 Joe Marcus Clarke <marcus@FreeBSD.org>
++ * Copyright (C) 2005 Jean-Yves Lefort <jylefort@brutele.be>
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
@@ -22,6 +23,7 @@
 +
 +#include <config.h>
 +#include <sys/types.h>
++#include <sys/stat.h>
 +#include <sys/event.h>
 +#include <sys/time.h>
 +#include <fcntl.h>
@@ -47,6 +49,23 @@
 +    GSList *dirlist;
 +} KQueueData;
 +
++typedef struct
++{
++    ino_t ino;
++    mode_t mode;
++    uid_t uid;
++    gid_t gid;
++    time_t mtime;
++    time_t ctime;
++    off_t size;
++} MiniStat;
++
++typedef struct {
++    char *filename;
++    char *pathname;
++    MiniStat sb;
++} FileData;
++  
 +static GHashTable *dir_path_hash = NULL;
 +static GHashTable *file_path_hash = NULL;
 +static GHashTable *fd_hash = NULL;
@@ -80,70 +99,44 @@
 +    return data;
 +}
 +
-+static GSList *
-+gam_kqueue_lsdir(const char *path)
++static void
++gam_kqueue_mini_stat (const char *pathname, MiniStat *mini_sb)
 +{
-+    GDir *dir;
-+    GSList *lst = NULL;
-+    const gchar *entry;
-+
-+    if (!path)
-+        return NULL;
++    struct stat sb;
 +
-+    dir = g_dir_open(path, 0, NULL);
-+    if (!dir)
-+        return NULL;
-+
-+    entry = g_dir_read_name(dir);
-+
-+    while (entry) {
-+        lst = g_slist_prepend(lst, g_strdup(entry));
-+        entry = g_dir_read_name(dir);
++    if (lstat(pathname, &sb) == 0) {
++        mini_sb->ino = sb.st_ino;
++	mini_sb->mode = sb.st_mode;
++	mini_sb->uid = sb.st_uid;
++	mini_sb->gid = sb.st_gid;
++        mini_sb->mtime = sb.st_mtime;
++	mini_sb->ctime = sb.st_ctime;
++	mini_sb->size = sb.st_size;
++    } else {
++        memset(mini_sb, 0, sizeof(*mini_sb));
 +    }
-+
-+    g_dir_close(dir);
-+
-+    return lst;
 +}
 +
-+static void
-+gam_kqueue_cmplst(GSList *lst1, GSList *lst2, GSList **added, GSList **deleted)
++static FileData *
++gam_kqueue_file_data_new (const char *path, const char *filename)
 +{
-+    int found;
-+    GSList *l;
-+
-+    if (!lst1 && !lst2)
-+        return;
++    FileData *fdata;
++    struct stat sb;
 +
-+    if (!lst1) {
-+        *added = g_slist_copy(lst2);
-+        return;
-+    }
-+
-+    if (!lst2) {
-+        *deleted = g_slist_copy(lst1);
-+        return;
-+    }
++    fdata = g_new(FileData, 1);
++    fdata->filename = g_strdup(filename);
++    fdata->pathname = g_build_filename(path, filename, NULL);
++    gam_kqueue_mini_stat(fdata->pathname, &fdata->sb);
 +
-+    for (l = lst1; l; l = l->next) {
-+        found = 0;
-+        if (g_slist_find_custom(lst2, l->data, (GCompareFunc)strcmp)) {
-+            found = 1;
-+        }
-+        if (found == 0) {
-+            *deleted = g_slist_prepend(*deleted, l->data);
-+        }
-+    }
++    return fdata;
++}
 +
-+    for (l = lst2; l; l = l->next) {
-+        found = 0;
-+        if (g_slist_find_custom(lst1, l->data, (GCompareFunc)strcmp)) {
-+            found = 1;
-+        }
-+        if (found == 0) {
-+            *added = g_slist_prepend(*added, l->data);
-+        }
-+    }
++static void
++gam_kqueue_file_data_free (FileData *fdata)
++{
++    g_free(fdata->filename);
++    g_free(fdata->pathname);
++    g_free(fdata);
 +}
 +
 +static void
@@ -151,7 +144,7 @@
 +{
 +    g_free(data->path);
 +    if (data->dirlist) {
-+        g_slist_foreach(data->dirlist, (GFunc)g_free, NULL);
++        g_slist_foreach(data->dirlist, (GFunc)gam_kqueue_file_data_free, NULL);
 +        g_slist_free(data->dirlist);
 +    }
 +    if (data->subs) {
@@ -160,6 +153,12 @@
 +    g_free(data);
 +}
 +
++static int
++gam_kqueue_dirlist_find (FileData *fdata, const char *filename)
++{
++    return strcmp(fdata->filename, filename);
++}
++
 +static void
 +gam_kqueue_add_rm_handler(const char *path, GamSubscription *sub, gboolean added, gboolean was_missing)
 +{
@@ -230,21 +229,29 @@
 +            gam_server_emit_event (path, isdir, GAMIN_EVENT_CREATED, subs, 1);
 +	}
 +        if (gam_subscription_is_dir(sub) && isdir) {
-+	    GSList *l;
++	    GDir *dir;
 +
 +            data->isdir = TRUE;
-+            data->dirlist = gam_kqueue_lsdir(path);
++	    data->dirlist = NULL;
 +
-+	    for (l = data->dirlist; l; l = l->next) {
-+                char *tmpentry;
-+
-+                tmpentry = g_build_filename(path, l->data, NULL);
-+		if (!was_missing) {
-+		    gam_server_emit_event (tmpentry,
-+                        g_file_test(tmpentry, G_FILE_TEST_IS_DIR),
-+		        GAMIN_EVENT_EXISTS, subs, 1);
++	    dir = g_dir_open(path, 0, NULL);
++	    if (dir) {
++	        const char *entry;
++
++		while ((entry = g_dir_read_name(dir))) {
++		    FileData *fdata;
++
++		    fdata = gam_kqueue_file_data_new(path, entry);
++		    data->dirlist = g_slist_prepend(data->dirlist, fdata);
++
++		    if (!was_missing) {
++		        gam_server_emit_event(fdata->pathname,
++					      g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++					      GAMIN_EVENT_EXISTS, subs, 1);
++		    }
 +		}
-+		g_free(tmpentry);
++
++		g_dir_close(dir);
 +	    }
 +	}
 +
@@ -332,44 +339,50 @@
 +	isdir = g_file_test(data->path, G_FILE_TEST_IS_DIR);
 +
 +	if (gevent == GAMIN_EVENT_CHANGED && data->isdir) {
-+	    GSList *dirlist = NULL, *added = NULL, *deleted = NULL;
++	    GSList *dirlist = NULL;
 +	    GSList *l;
++	    GDir *dir;
++
++	    dir = g_dir_open(data->path, 0, NULL);
++	    if (dir) {
++	        const char *entry;
 +
-+	    dirlist = gam_kqueue_lsdir(data->path);
-+	    gam_kqueue_cmplst(data->dirlist, dirlist, &added, &deleted);
-+	    if (added || deleted) {
-+	        for (l = deleted; l; l = l->next) {
-+                    data->dirlist = g_slist_remove(data->dirlist, l->data);
-+		    event_path = g_build_filename(data->path, l->data, NULL);
-+		    g_free(l->data);
-+		    isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
-+
-+                    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED) , event_path);
-+
-+                    gam_server_emit_event (event_path, isdir,
-+                        GAMIN_EVENT_DELETED, data->subs, 1);
-+		    g_free(event_path);
++		while ((entry = g_dir_read_name(dir))) {
++		    dirlist = g_slist_prepend(dirlist, g_strdup(entry));
 +		}
 +
-+		for (l = added; l; l = l->next) {
-+                    dirlist = g_slist_remove(dirlist, l->data);
-+		    data->dirlist = g_slist_prepend(data->dirlist,
-+                        g_strdup(l->data));
-+		    event_path = g_build_filename(data->path, l->data, NULL);
-+		    g_free(l->data);
-+		    isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
-+
-+		    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED) , event_path);
-+
-+		    gam_server_emit_event (event_path, isdir,
-+                        GAMIN_EVENT_CREATED, data->subs, 1);
-+		    g_free(event_path);
++		g_dir_close(dir);
++	    }
++
++	    for (l = dirlist; l; l = l->next) {
++	        if (! g_slist_find_custom(data->dirlist, l->data, (GCompareFunc) gam_kqueue_dirlist_find)) {
++		    FileData *fdata;
++
++		    fdata = gam_kqueue_file_data_new(data->path, l->data);
++		    data->dirlist = g_slist_prepend(data->dirlist, fdata);
++
++		    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED), fdata->pathname);
++		    gam_server_emit_event(fdata->pathname,
++					  g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++					  GAMIN_EVENT_CREATED, data->subs, 1);
 +		}
++	    }
++
++	iterate:
++	    for (l = data->dirlist; l; l = l->next) {
++	        FileData *fdata = l->data;
++
++	        if (! g_slist_find_custom(dirlist, fdata->filename, (GCompareFunc) strcmp)) {
++		    data->dirlist = g_slist_remove(data->dirlist, fdata);
 +
-+		if (added)
-+                  g_slist_free(added);
-+		if (deleted)
-+                  g_slist_free(deleted);
++                    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED), fdata->pathname);
++		    gam_server_emit_event(fdata->pathname,
++					  g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++					  GAMIN_EVENT_DELETED, data->subs, 1);
++
++		    gam_kqueue_file_data_free(fdata);
++		    goto iterate; /* list changed, start again */
++		}
 +	    }
 +
 +	    if (dirlist) {
@@ -380,6 +393,22 @@
 +	}
 +	else {
 +	    event_path = g_strdup (data->path);
++
++	    if (gevent == GAMIN_EVENT_DELETED
++		|| gevent == GAMIN_EVENT_ENDEXISTS
++		|| gevent == GAMIN_EVENT_MOVED) {
++	      /* close and move to exist_list, to catch next creation */
++	      close(data->fd);
++	      if (data->isdir) {
++		  g_hash_table_remove(dir_path_hash, data->path);
++	      }
++	      else {
++		  g_hash_table_remove(file_path_hash, data->path);
++	      }
++	      g_hash_table_remove(fd_hash, GINT_TO_POINTER(data->fd));
++
++	      exist_list = g_slist_append(exist_list, data);
++	    }
 +	}
 +
 +        isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
@@ -418,8 +447,51 @@
 +    return TRUE;
 +}
 +
++static void
++gam_kqueue_dirlist_check_cb (const char *path, KQueueData *data, gpointer user_data)
++{
++    GSList *l;
++
++    for (l = data->dirlist; l; l = l->next) {
++        FileData *fdata = l->data;
++	MiniStat sb;
++
++	gam_kqueue_mini_stat(fdata->pathname, &sb);
++
++	if (sb.mtime != fdata->sb.mtime
++	    || sb.ctime != fdata->sb.ctime
++	    || sb.size != fdata->sb.size
++	    || sb.mode != fdata->sb.mode
++	    || sb.uid != fdata->sb.uid
++	    || sb.gid != fdata->sb.gid
++	    || sb.ino != fdata->sb.ino)
++	  {
++	      memcpy(&fdata->sb, &sb, sizeof(sb));
++
++	      GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CHANGED), fdata->pathname);
++	      gam_server_emit_event(fdata->pathname,
++				    g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++				    GAMIN_EVENT_CHANGED, data->subs, 1);
++	  }
++    }
++}
++
++static gboolean
++gam_kqueue_dirlist_check (gpointer user_data)
++{
++    G_LOCK(kqueue);
++
++    GAM_DEBUG(DEBUG_INFO, "gam_kqueue_dirlist_check()\n");
++
++    g_hash_table_foreach(dir_path_hash, (GHFunc) gam_kqueue_dirlist_check_cb, NULL);
++
++    G_UNLOCK(kqueue);
++
++    return TRUE;
++}
++
 +static gboolean
-+gam_kqueue_event_handler (gpointer user_data)
++gam_kqueue_event_handler (GIOChannel *source, GIOCondition condition, gpointer user_data)
 +{
 +    KQueueData *data;
 +    struct kevent ev[1];
@@ -531,6 +603,8 @@
 +gboolean
 +gam_kqueue_init(void)
 +{
++    GIOChannel *channel;
++
 +    kq = kqueue();
 +    if (kq == -1) {
 +	GAM_DEBUG(DEBUG_INFO, "Could not initialize a new kqueue\n");
@@ -538,11 +612,22 @@
 +    }
 +
 +    g_timeout_add(1000, gam_kqueue_exist_check, NULL);
-+    g_timeout_add(1000, gam_kqueue_event_handler, NULL);
++
++    channel = g_io_channel_unix_new(kq);
++    g_io_add_watch(channel, G_IO_IN, gam_kqueue_event_handler, NULL);
 +
 +    dir_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
 +    file_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
 +    fd_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
++
++    /*
++     * gam_kqueue_dirlist_check() has to lstat() every file in every
++     * monitored directory. This can easily become an intensive task
++     * if a few large directories are monitored (for instance a mail
++     * checker monitoring a couple of MH folders), therefore we use a
++     * reasonable poll interval (6 seconds, same as FAM's default).
++     */
++    g_timeout_add(6000, gam_kqueue_dirlist_check, NULL);
 +
 +    GAM_DEBUG(DEBUG_INFO, "kqueue initialized\n");
 +
Comment 3 Jean-Yves Lefort 2005-04-06 21:54:42 UTC
Update: save some memory by not allocating fdata->filename; remove
useless stat buffer in gam_kqueue_file_data_new().

diff -ruN /usr/ports/devel/gamin/Makefile gamin/Makefile
--- /usr/ports/devel/gamin/Makefile	Sat Apr  2 11:08:46 2005
+++ gamin/Makefile	Wed Apr  6 18:57:30 2005
@@ -7,7 +7,7 @@
 
 PORTNAME=	gamin
 PORTVERSION=	0.0.26
-PORTREVISION?=	8
+PORTREVISION?=	9
 CATEGORIES?=	devel
 MASTER_SITES=	http://www.gnome.org/~veillard/gamin/sources/
 
diff -ruN /usr/ports/devel/gamin/files/patch-server_gam_kqueue.c gamin/files/patch-server_gam_kqueue.c
--- /usr/ports/devel/gamin/files/patch-server_gam_kqueue.c	Sat Apr  2 11:08:47 2005
+++ gamin/files/patch-server_gam_kqueue.c	Wed Apr  6 22:47:50 2005
@@ -1,8 +1,9 @@
---- server/gam_kqueue.c.orig	Thu Mar 31 20:39:54 2005
-+++ server/gam_kqueue.c	Fri Apr  1 01:09:11 2005
-@@ -0,0 +1,636 @@
+--- server/gam_kqueue.c.orig	Wed Apr  6 22:46:40 2005
++++ server/gam_kqueue.c	Wed Apr  6 22:47:16 2005
+@@ -0,0 +1,720 @@
 +/*
 + * Copyright (C) 2005 Joe Marcus Clarke <marcus@FreeBSD.org>
++ * Copyright (C) 2005 Jean-Yves Lefort <jylefort@brutele.be>
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
@@ -22,6 +23,7 @@
 +
 +#include <config.h>
 +#include <sys/types.h>
++#include <sys/stat.h>
 +#include <sys/event.h>
 +#include <sys/time.h>
 +#include <fcntl.h>
@@ -47,6 +49,23 @@
 +    GSList *dirlist;
 +} KQueueData;
 +
++typedef struct
++{
++    ino_t ino;
++    mode_t mode;
++    uid_t uid;
++    gid_t gid;
++    time_t mtime;
++    time_t ctime;
++    off_t size;
++} MiniStat;
++
++typedef struct {
++    char *pathname;
++    char *filename;		/* pointer into pathname */
++    MiniStat sb;
++} FileData;
++  
 +static GHashTable *dir_path_hash = NULL;
 +static GHashTable *file_path_hash = NULL;
 +static GHashTable *fd_hash = NULL;
@@ -80,70 +99,43 @@
 +    return data;
 +}
 +
-+static GSList *
-+gam_kqueue_lsdir(const char *path)
++static void
++gam_kqueue_mini_stat (const char *pathname, MiniStat *mini_sb)
 +{
-+    GDir *dir;
-+    GSList *lst = NULL;
-+    const gchar *entry;
-+
-+    if (!path)
-+        return NULL;
++    struct stat sb;
 +
-+    dir = g_dir_open(path, 0, NULL);
-+    if (!dir)
-+        return NULL;
-+
-+    entry = g_dir_read_name(dir);
-+
-+    while (entry) {
-+        lst = g_slist_prepend(lst, g_strdup(entry));
-+        entry = g_dir_read_name(dir);
++    if (lstat(pathname, &sb) == 0) {
++        mini_sb->ino = sb.st_ino;
++	mini_sb->mode = sb.st_mode;
++	mini_sb->uid = sb.st_uid;
++	mini_sb->gid = sb.st_gid;
++        mini_sb->mtime = sb.st_mtime;
++	mini_sb->ctime = sb.st_ctime;
++	mini_sb->size = sb.st_size;
++    } else {
++        memset(mini_sb, 0, sizeof(*mini_sb));
 +    }
-+
-+    g_dir_close(dir);
-+
-+    return lst;
 +}
 +
-+static void
-+gam_kqueue_cmplst(GSList *lst1, GSList *lst2, GSList **added, GSList **deleted)
++static FileData *
++gam_kqueue_file_data_new (const char *path, const char *filename)
 +{
-+    int found;
-+    GSList *l;
-+
-+    if (!lst1 && !lst2)
-+        return;
++    FileData *fdata;
 +
-+    if (!lst1) {
-+        *added = g_slist_copy(lst2);
-+        return;
-+    }
-+
-+    if (!lst2) {
-+        *deleted = g_slist_copy(lst1);
-+        return;
-+    }
++    fdata = g_new(FileData, 1);
++    fdata->pathname = g_build_filename(path, filename, NULL);
++    fdata->filename = strrchr(fdata->pathname, G_DIR_SEPARATOR);
++    fdata->filename = fdata->filename ? fdata->filename + 1 : fdata->pathname;
++    gam_kqueue_mini_stat(fdata->pathname, &fdata->sb);
 +
-+    for (l = lst1; l; l = l->next) {
-+        found = 0;
-+        if (g_slist_find_custom(lst2, l->data, (GCompareFunc)strcmp)) {
-+            found = 1;
-+        }
-+        if (found == 0) {
-+            *deleted = g_slist_prepend(*deleted, l->data);
-+        }
-+    }
++    return fdata;
++}
 +
-+    for (l = lst2; l; l = l->next) {
-+        found = 0;
-+        if (g_slist_find_custom(lst1, l->data, (GCompareFunc)strcmp)) {
-+            found = 1;
-+        }
-+        if (found == 0) {
-+            *added = g_slist_prepend(*added, l->data);
-+        }
-+    }
++static void
++gam_kqueue_file_data_free (FileData *fdata)
++{
++    g_free(fdata->pathname);
++    g_free(fdata);
 +}
 +
 +static void
@@ -151,7 +143,7 @@
 +{
 +    g_free(data->path);
 +    if (data->dirlist) {
-+        g_slist_foreach(data->dirlist, (GFunc)g_free, NULL);
++        g_slist_foreach(data->dirlist, (GFunc)gam_kqueue_file_data_free, NULL);
 +        g_slist_free(data->dirlist);
 +    }
 +    if (data->subs) {
@@ -160,6 +152,12 @@
 +    g_free(data);
 +}
 +
++static int
++gam_kqueue_dirlist_find (FileData *fdata, const char *filename)
++{
++    return strcmp(fdata->filename, filename);
++}
++
 +static void
 +gam_kqueue_add_rm_handler(const char *path, GamSubscription *sub, gboolean added, gboolean was_missing)
 +{
@@ -230,21 +228,29 @@
 +            gam_server_emit_event (path, isdir, GAMIN_EVENT_CREATED, subs, 1);
 +	}
 +        if (gam_subscription_is_dir(sub) && isdir) {
-+	    GSList *l;
++	    GDir *dir;
 +
 +            data->isdir = TRUE;
-+            data->dirlist = gam_kqueue_lsdir(path);
++	    data->dirlist = NULL;
 +
-+	    for (l = data->dirlist; l; l = l->next) {
-+                char *tmpentry;
-+
-+                tmpentry = g_build_filename(path, l->data, NULL);
-+		if (!was_missing) {
-+		    gam_server_emit_event (tmpentry,
-+                        g_file_test(tmpentry, G_FILE_TEST_IS_DIR),
-+		        GAMIN_EVENT_EXISTS, subs, 1);
++	    dir = g_dir_open(path, 0, NULL);
++	    if (dir) {
++	        const char *entry;
++
++		while ((entry = g_dir_read_name(dir))) {
++		    FileData *fdata;
++
++		    fdata = gam_kqueue_file_data_new(path, entry);
++		    data->dirlist = g_slist_prepend(data->dirlist, fdata);
++
++		    if (!was_missing) {
++		        gam_server_emit_event(fdata->pathname,
++					      g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++					      GAMIN_EVENT_EXISTS, subs, 1);
++		    }
 +		}
-+		g_free(tmpentry);
++
++		g_dir_close(dir);
 +	    }
 +	}
 +
@@ -332,44 +338,50 @@
 +	isdir = g_file_test(data->path, G_FILE_TEST_IS_DIR);
 +
 +	if (gevent == GAMIN_EVENT_CHANGED && data->isdir) {
-+	    GSList *dirlist = NULL, *added = NULL, *deleted = NULL;
++	    GSList *dirlist = NULL;
 +	    GSList *l;
++	    GDir *dir;
++
++	    dir = g_dir_open(data->path, 0, NULL);
++	    if (dir) {
++	        const char *entry;
 +
-+	    dirlist = gam_kqueue_lsdir(data->path);
-+	    gam_kqueue_cmplst(data->dirlist, dirlist, &added, &deleted);
-+	    if (added || deleted) {
-+	        for (l = deleted; l; l = l->next) {
-+                    data->dirlist = g_slist_remove(data->dirlist, l->data);
-+		    event_path = g_build_filename(data->path, l->data, NULL);
-+		    g_free(l->data);
-+		    isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
-+
-+                    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED) , event_path);
-+
-+                    gam_server_emit_event (event_path, isdir,
-+                        GAMIN_EVENT_DELETED, data->subs, 1);
-+		    g_free(event_path);
++		while ((entry = g_dir_read_name(dir))) {
++		    dirlist = g_slist_prepend(dirlist, g_strdup(entry));
 +		}
 +
-+		for (l = added; l; l = l->next) {
-+                    dirlist = g_slist_remove(dirlist, l->data);
-+		    data->dirlist = g_slist_prepend(data->dirlist,
-+                        g_strdup(l->data));
-+		    event_path = g_build_filename(data->path, l->data, NULL);
-+		    g_free(l->data);
-+		    isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
-+
-+		    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED) , event_path);
-+
-+		    gam_server_emit_event (event_path, isdir,
-+                        GAMIN_EVENT_CREATED, data->subs, 1);
-+		    g_free(event_path);
++		g_dir_close(dir);
++	    }
++
++	    for (l = dirlist; l; l = l->next) {
++	        if (! g_slist_find_custom(data->dirlist, l->data, (GCompareFunc) gam_kqueue_dirlist_find)) {
++		    FileData *fdata;
++
++		    fdata = gam_kqueue_file_data_new(data->path, l->data);
++		    data->dirlist = g_slist_prepend(data->dirlist, fdata);
++
++		    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED), fdata->pathname);
++		    gam_server_emit_event(fdata->pathname,
++					  g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++					  GAMIN_EVENT_CREATED, data->subs, 1);
 +		}
++	    }
++
++	iterate:
++	    for (l = data->dirlist; l; l = l->next) {
++	        FileData *fdata = l->data;
++
++	        if (! g_slist_find_custom(dirlist, fdata->filename, (GCompareFunc) strcmp)) {
++		    data->dirlist = g_slist_remove(data->dirlist, fdata);
 +
-+		if (added)
-+                  g_slist_free(added);
-+		if (deleted)
-+                  g_slist_free(deleted);
++                    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED), fdata->pathname);
++		    gam_server_emit_event(fdata->pathname,
++					  g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++					  GAMIN_EVENT_DELETED, data->subs, 1);
++
++		    gam_kqueue_file_data_free(fdata);
++		    goto iterate; /* list changed, start again */
++		}
 +	    }
 +
 +	    if (dirlist) {
@@ -380,6 +392,22 @@
 +	}
 +	else {
 +	    event_path = g_strdup (data->path);
++
++	    if (gevent == GAMIN_EVENT_DELETED
++		|| gevent == GAMIN_EVENT_ENDEXISTS
++		|| gevent == GAMIN_EVENT_MOVED) {
++	      /* close and move to exist_list, to catch next creation */
++	      close(data->fd);
++	      if (data->isdir) {
++		  g_hash_table_remove(dir_path_hash, data->path);
++	      }
++	      else {
++		  g_hash_table_remove(file_path_hash, data->path);
++	      }
++	      g_hash_table_remove(fd_hash, GINT_TO_POINTER(data->fd));
++
++	      exist_list = g_slist_append(exist_list, data);
++	    }
 +	}
 +
 +        isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
@@ -418,8 +446,51 @@
 +    return TRUE;
 +}
 +
++static void
++gam_kqueue_dirlist_check_cb (const char *path, KQueueData *data, gpointer user_data)
++{
++    GSList *l;
++
++    for (l = data->dirlist; l; l = l->next) {
++        FileData *fdata = l->data;
++	MiniStat sb;
++
++	gam_kqueue_mini_stat(fdata->pathname, &sb);
++
++	if (sb.mtime != fdata->sb.mtime
++	    || sb.ctime != fdata->sb.ctime
++	    || sb.size != fdata->sb.size
++	    || sb.mode != fdata->sb.mode
++	    || sb.uid != fdata->sb.uid
++	    || sb.gid != fdata->sb.gid
++	    || sb.ino != fdata->sb.ino)
++	  {
++	      memcpy(&fdata->sb, &sb, sizeof(sb));
++
++	      GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CHANGED), fdata->pathname);
++	      gam_server_emit_event(fdata->pathname,
++				    g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
++				    GAMIN_EVENT_CHANGED, data->subs, 1);
++	  }
++    }
++}
++
++static gboolean
++gam_kqueue_dirlist_check (gpointer user_data)
++{
++    G_LOCK(kqueue);
++
++    GAM_DEBUG(DEBUG_INFO, "gam_kqueue_dirlist_check()\n");
++
++    g_hash_table_foreach(dir_path_hash, (GHFunc) gam_kqueue_dirlist_check_cb, NULL);
++
++    G_UNLOCK(kqueue);
++
++    return TRUE;
++}
++
 +static gboolean
-+gam_kqueue_event_handler (gpointer user_data)
++gam_kqueue_event_handler (GIOChannel *source, GIOCondition condition, gpointer user_data)
 +{
 +    KQueueData *data;
 +    struct kevent ev[1];
@@ -531,6 +602,8 @@
 +gboolean
 +gam_kqueue_init(void)
 +{
++    GIOChannel *channel;
++
 +    kq = kqueue();
 +    if (kq == -1) {
 +	GAM_DEBUG(DEBUG_INFO, "Could not initialize a new kqueue\n");
@@ -538,11 +611,22 @@
 +    }
 +
 +    g_timeout_add(1000, gam_kqueue_exist_check, NULL);
-+    g_timeout_add(1000, gam_kqueue_event_handler, NULL);
++
++    channel = g_io_channel_unix_new(kq);
++    g_io_add_watch(channel, G_IO_IN, gam_kqueue_event_handler, NULL);
 +
 +    dir_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
 +    file_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
 +    fd_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
++
++    /*
++     * gam_kqueue_dirlist_check() has to lstat() every file in every
++     * monitored directory. This can easily become an intensive task
++     * if a few large directories are monitored (for instance a mail
++     * checker monitoring a couple of MH folders), therefore we use a
++     * reasonable poll interval (6 seconds, same as FAM's default).
++     */
++    g_timeout_add(6000, gam_kqueue_dirlist_check, NULL);
 +
 +    GAM_DEBUG(DEBUG_INFO, "kqueue initialized\n");
 +
Comment 4 Joe Marcus Clarke freebsd_committer freebsd_triage 2005-04-07 02:18:33 UTC
State Changed
From-To: open->closed

Committed, thanks!