ports/79605: Update port: devel/gamin (fix and improve the kqueue backend)

Jean-Yves Lefort jylefort at brutele.be
Wed Apr 6 12:00:18 PDT 2005


The following reply was made to PR ports/79605; it has been noted by GNATS.

From: Jean-Yves Lefort <jylefort at brutele.be>
To: freebsd-gnats-submit at FreeBSD.org
Cc:  
Subject: Re: ports/79605: Update port: devel/gamin (fix and improve the
 kqueue backend)
Date: Wed, 6 Apr 2005 20:53:02 +0200

 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 at FreeBSD.org>
 ++ * Copyright (C) 2005 Jean-Yves Lefort <jylefort at 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");
  +
 


More information about the freebsd-gnome mailing list