glib r6702 - in trunk: . gio gio/fen



Author: alexl
Date: Fri Mar 14 08:58:24 2008
New Revision: 6702
URL: http://svn.gnome.org/viewvc/glib?rev=6702&view=rev

Log:
2008-03-14  Alexander Larsson  <alexl redhat com>

        * Makefile.am:
        * fen/Makefile.am: Added.
        * fen/fen-data.[ch]: Added.
        * fen/fen-dump.[ch]: Added.
        * fen/fen-helper.[ch]: Added.
        * fen/fen-kernel.[ch]: Added.
        * fen/fen-missing.[ch]: Added.
        * fen/fen-node.[ch]: Added.
        * fen/fen-sub.[ch]: Added.
        * fen/gfendirectorymonitor.[ch]: Added.
        * fen/gfenfilemonitor.[ch]: Added.
        * giomodule.c:
	Added Solaris FEN file notification backend.
	Patch from Lin Ma <Lin Ma Sun COM>



Added:
   trunk/gio/fen/
   trunk/gio/fen/Makefile.am
   trunk/gio/fen/fen-data.c
   trunk/gio/fen/fen-data.h
   trunk/gio/fen/fen-dump.c
   trunk/gio/fen/fen-dump.h
   trunk/gio/fen/fen-helper.c
   trunk/gio/fen/fen-helper.h
   trunk/gio/fen/fen-kernel.c
   trunk/gio/fen/fen-kernel.h
   trunk/gio/fen/fen-missing.c
   trunk/gio/fen/fen-missing.h
   trunk/gio/fen/fen-node.c
   trunk/gio/fen/fen-node.h
   trunk/gio/fen/fen-sub.c
   trunk/gio/fen/fen-sub.h
   trunk/gio/fen/gfendirectorymonitor.c
   trunk/gio/fen/gfendirectorymonitor.h
   trunk/gio/fen/gfenfilemonitor.c
   trunk/gio/fen/gfenfilemonitor.h
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/gio/ChangeLog
   trunk/gio/Makefile.am
   trunk/gio/giomodule.c

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Fri Mar 14 08:58:24 2008
@@ -1502,6 +1502,22 @@
 
 AM_CONDITIONAL(HAVE_INOTIFY, [test "$inotify_support" = "yes"])
 
+dnl *********************************
+dnl ** Check for Solaris FEN (GIO) **
+dnl *********************************
+fen_support=no
+AC_COMPILE_IFELSE([ 
+#include <port.h> 
+#ifndef PORT_SOURCE_FILE 
+#error "Please upgrade to Nevada 72 or above to suppoert FEN" 
+#endif 
+int main() { return 0; } ],
+[
+	fen_support=yes
+],)
+
+AM_CONDITIONAL(HAVE_FEN, [test "$fen_support" = "yes"])
+
 dnl ****************************
 dnl *** Checks for FAM (GIO) ***
 dnl ****************************
@@ -3234,6 +3250,7 @@
 gio/Makefile
 gio/xdgmime/Makefile
 gio/inotify/Makefile
+gio/fen/Makefile
 gio/fam/Makefile
 gio/win32/Makefile
 gio/tests/Makefile

Modified: trunk/gio/Makefile.am
==============================================================================
--- trunk/gio/Makefile.am	(original)
+++ trunk/gio/Makefile.am	Fri Mar 14 08:58:24 2008
@@ -99,6 +99,12 @@
 platform_libadd += inotify/libinotify.la
 endif
 
+if HAVE_FEN
+AM_CPPFLAGS += -DHAVE_FEN
+SUBDIRS += fen
+platform_libadd += fen/libfen.la
+endif
+
 if OS_WIN32
 SUBDIRS += win32
 platform_libadd += win32/libgiowin32.la

Added: trunk/gio/fen/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/gio/fen/Makefile.am	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,38 @@
+include $(top_srcdir)/Makefile.decl
+
+NULL =
+
+noinst_LTLIBRARIES = libfen.la
+
+libfen_la_SOURCES = 		\
+	fen-dump.c		\
+	fen-dump.h		\
+	fen-kernel.c 		\
+	fen-kernel.h 		\
+	fen-node.c		\
+	fen-node.h		\
+	fen-missing.c		\
+	fen-missing.h		\
+	fen-helper.c 		\
+	fen-helper.h		\
+	fen-data.c		\
+	fen-data.h		\
+	fen-sub.c		\
+	fen-sub.h		\
+	gfenfilemonitor.c		\
+	gfenfilemonitor.h		\
+	gfendirectorymonitor.c	\
+	gfendirectorymonitor.h	\
+	$(NULL)
+
+libfen_la_CFLAGS = \
+	-DG_LOG_DOMAIN=\"GLib-GIO\"	\
+	-I$(top_srcdir) 		\
+	-I$(top_srcdir)/glib 		\
+	-I$(top_srcdir)/gmodule		\
+	-I$(top_srcdir)/gio 		\
+	-I$(top_builddir)/gio 		\
+	$(GLIB_DEBUG_FLAGS)		\
+	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\
+	-DGIO_COMPILATION		\
+	-DG_DISABLE_DEPRECATED

Added: trunk/gio/fen/fen-data.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-data.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,708 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "config.h"
+#include <port.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <glib.h>
+#include "fen-data.h"
+#include "fen-kernel.h"
+#include "fen-missing.h"
+#include "fen-dump.h"
+
+#define	PROCESS_EVENTQ_TIME	10	/* in milliseconds */
+#define PAIR_EVENTS_TIMEVAL	00000	/* in microseconds */
+#define	PAIR_EVENTS_INC_TIMEVAL	0000	/* in microseconds */
+#define SCAN_CHANGINGS_TIME	50	/* in milliseconds */
+#define	SCAN_CHANGINGS_MAX_TIME	(4*100)	/* in milliseconds */
+#define	SCAN_CHANGINGS_MIN_TIME	(4*100)	/* in milliseconds */
+#define	INIT_CHANGES_NUM	2
+#define	BASE_NUM	2
+
+#define FD_W if (fd_debug_enabled) g_warning
+static gboolean fd_debug_enabled = FALSE;
+
+G_LOCK_EXTERN (fen_lock);
+static GList *deleting_data = NULL;
+static guint deleting_data_id = 0;
+
+static void (*emit_once_cb) (fdata *f, int events, gpointer sub);
+static void (*emit_cb) (fdata *f, int events);
+static int (*_event_converter) (int event);
+
+static gboolean fdata_delete (fdata* f);
+static gint fdata_sub_find (gpointer a, gpointer b);
+static void scan_children (node_t *f);
+static void scan_known_children (node_t* f);
+
+node_t*
+add_missing_cb (node_t* parent, gpointer user_data)
+{
+    g_assert (parent);
+    FD_W ("%s p:0x%p %s\n", __func__, parent, (gchar*)user_data);
+    return add_node (parent, (gchar*)user_data);
+}
+
+gboolean
+pre_del_cb (node_t* node, gpointer user_data)
+{
+    fdata* data;
+    
+    g_assert (node);
+    data = node_get_data (node);
+    FD_W ("%s node:0x%p %s\n", __func__, node, NODE_NAME(node));
+    if (data != NULL) {
+        if (!FN_IS_PASSIVE(data)) {
+            return FALSE;
+        }
+        fdata_delete (data);
+    }
+    return TRUE;
+}
+
+static guint
+_pow (guint x, guint y)
+{
+    guint z = 1;
+    g_assert (x >= 0 && y >= 0);
+    for (; y > 0; y--) {
+        z *= x;
+    }
+    return z;
+}
+
+static guint
+get_scalable_scan_time (fdata* data)
+{
+    guint sleep_time;
+    /* Caculate from num = 0 */
+    sleep_time = _pow (BASE_NUM, data->changed_event_num) * SCAN_CHANGINGS_TIME;
+    if (sleep_time < SCAN_CHANGINGS_MIN_TIME) {
+        sleep_time = SCAN_CHANGINGS_MIN_TIME;
+    } else if (sleep_time > SCAN_CHANGINGS_MAX_TIME) {
+        sleep_time = SCAN_CHANGINGS_MAX_TIME;
+        data->change_update_id = INIT_CHANGES_NUM;
+    }
+    FD_W ("SCALABE SCAN num:time [ %4u : %4u ] %s\n", data->changed_event_num, sleep_time, FN_NAME(data));
+    return sleep_time;
+}
+
+static gboolean
+g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
+{
+    if (val1->tv_sec < val2->tv_sec)
+        return TRUE;
+  
+    if (val1->tv_sec > val2->tv_sec)
+        return FALSE;
+  
+    /* val1->tv_sec == val2->tv_sec */
+    if (val1->tv_usec < val2->tv_usec)
+        return TRUE;
+  
+    return FALSE;
+}
+
+/**
+ * If all active children nodes are ported, then cancel monitor the parent node
+ *
+ * Unsafe, need lock.
+ */
+static void
+scan_known_children (node_t* f)
+{
+	GDir *dir;
+	GError *err = NULL;
+    fdata* pdata;
+    
+    FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
+    pdata = node_get_data (f);
+    /*
+     * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
+     */
+	dir = g_dir_open (NODE_NAME(f), 0, &err);
+	if (dir) {
+		const char *basename;
+        
+		while ((basename = g_dir_read_name (dir)))
+		{
+            node_t* childf = NULL;
+            fdata* data;
+			GList *idx;
+			/*
+             * If the node is existed, and isn't ported, then emit created
+             * event. Ignore others.
+             */
+            childf = children_find (f, basename);
+            if (childf &&
+              (data = node_get_data (childf)) != NULL &&
+              !FN_IS_PASSIVE (data)) {
+                if (!is_monitoring (data) &&
+                  port_add (&data->fobj, &data->len, data)) {
+                    fdata_emit_events (data, FN_EVENT_CREATED);
+                }
+            }
+        }
+		g_dir_close (dir);
+    } else {
+        FD_W (err->message);
+        g_error_free (err);
+    }
+}
+
+static void
+scan_children (node_t *f)
+{
+	GDir *dir;
+	GError *err = NULL;
+    fdata* pdata;
+    
+    FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
+    pdata = node_get_data (f);
+    /*
+     * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
+     */
+	dir = g_dir_open (NODE_NAME(f), 0, &err);
+	if (dir) {
+		const char *basename;
+        
+		while ((basename = g_dir_read_name (dir)))
+		{
+            node_t* childf = NULL;
+            fdata* data;
+			GList *idx;
+
+            childf = children_find (f, basename);
+            if (childf == NULL) {
+                gchar *filename;
+
+                filename = g_build_filename (NODE_NAME(f), basename, NULL);
+                childf = add_node (f, filename);
+                g_assert (childf);
+                data = fdata_new (childf, FALSE);
+                g_free (filename);
+            }
+            if ((data = node_get_data (childf)) == NULL) {
+                data = fdata_new (childf, FALSE);
+            }
+            /* Be sure data isn't ported and add to port successfully */
+            /* Don't need delete it, it will be deleted by the parent */
+            if (is_monitoring (data)) {
+                /* Ignored */
+            } else if (/* !is_ported (data) && */
+                port_add (&data->fobj, &data->len, data)) {
+                fdata_emit_events (data, FN_EVENT_CREATED);
+            }
+        }
+		g_dir_close (dir);
+    } else {
+        FD_W (err->message);
+        g_error_free (err);
+    }
+}
+
+static gboolean
+scan_deleting_data (gpointer data)
+{
+    fdata *f;
+    GList* i;
+    GList* deleted_list = NULL;
+    gboolean ret = TRUE;
+
+    if (G_TRYLOCK (fen_lock)) {
+        for (i = deleting_data; i; i = i->next) {
+            f = (fdata*)i->data;
+            if (fdata_delete (f)) {
+                deleted_list = g_list_prepend (deleted_list, i);
+            }
+        }
+
+        for (i = deleted_list; i; i = i->next) {
+            deleting_data = g_list_remove_link (deleting_data,
+              (GList *)i->data);
+            g_list_free_1 ((GList *)i->data);
+        }
+        g_list_free (deleted_list);
+
+        if (deleting_data == NULL) {
+            deleting_data_id = 0;
+            ret = FALSE;
+        }
+        G_UNLOCK (fen_lock);
+    }
+    return ret;
+}
+
+gboolean
+is_monitoring (fdata* data)
+{
+    return is_ported (data) || data->change_update_id > 0;
+}
+
+fdata*
+get_parent_data (fdata* data)
+{
+    if (FN_NODE(data) && !IS_TOPNODE(FN_NODE(data))) {
+        return node_get_data (FN_NODE(data)->parent);
+    }
+    return NULL;
+}
+
+node_t*
+get_parent_node (fdata* data)
+{
+    if (FN_NODE(data)) {
+        return (FN_NODE(data)->parent);
+    }
+    return NULL;
+}
+
+fdata *
+fdata_new (node_t* node, gboolean is_mondir)
+{
+	fdata *f = NULL;
+
+    g_assert (node);
+	if ((f = g_new0 (fdata, 1)) != NULL) {
+        FN_NODE(f) = node;
+		FN_NAME(f) = g_strdup (NODE_NAME(node));
+        f->is_dir = is_mondir;
+        f->eventq = g_queue_new ();
+        FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
+        node_set_data (node, f);
+	}
+	return f;
+}
+
+static gboolean
+fdata_delete (fdata *f)
+{
+    fnode_event_t *ev;
+
+    FD_W ("[ TRY %s ] 0x%p id[%4d:%4d] %s\n", __func__, f, f->eventq_id, f->change_update_id, FN_NAME(f));
+    g_assert (FN_IS_PASSIVE(f));
+
+    port_remove (f);
+    /* missing_remove (f); */
+
+    if (f->node != NULL) {
+        node_set_data (f->node, NULL);
+        f->node = NULL;
+    }
+
+    if (f->change_update_id > 0 || f->eventq_id > 0) {
+        if (FN_IS_LIVING(f)) {
+            f->is_cancelled = TRUE;
+            deleting_data = g_list_prepend (deleting_data, f);
+            if (deleting_data_id == 0) {
+                deleting_data_id = g_idle_add (scan_deleting_data, NULL);
+                g_assert (deleting_data_id > 0);
+            }
+        }
+        return FALSE;
+    }
+    FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
+
+    while ((ev = g_queue_pop_head (f->eventq)) != NULL) {
+        fnode_event_delete (ev);
+    }
+
+    g_queue_free (f->eventq);
+    g_free (FN_NAME(f));
+    g_free (f);
+    return TRUE;
+}
+
+void
+fdata_reset (fdata* data)
+{
+    fnode_event_t *ev;
+
+    g_assert (data);
+
+    while ((ev = g_queue_pop_head (data->eventq)) != NULL) {
+        fnode_event_delete (ev);
+    }
+}
+
+static gint
+fdata_sub_find (gpointer a, gpointer b)
+{
+    if (a != b) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+void
+fdata_sub_add (fdata *f, gpointer sub)
+{
+    FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
+    g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) == NULL);
+    f->subs = g_list_prepend (f->subs, sub);
+}
+
+void
+fdata_sub_remove (fdata *f, gpointer sub)
+{
+    GList *l;
+    FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
+    g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) != NULL);
+    l = g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find);
+    g_assert (l);
+    g_assert (sub == l->data);
+    f->subs = g_list_delete_link (f->subs, l);
+}
+
+/**
+ * Adjust self on failing to Port
+ */
+void
+fdata_adjust_deleted (fdata* f)
+{
+    node_t* parent;
+    fdata* pdata;
+    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
+
+    /*
+     * It's a top node. We move it to missing list.
+     */
+    parent = get_parent_node (f);
+    pdata = get_parent_data (f);
+    if (!FN_IS_PASSIVE(f) ||
+      children_num (FN_NODE(f)) > 0 ||
+      (pdata && !FN_IS_PASSIVE(pdata))) {
+        if (parent) {
+            if (pdata == NULL) {
+                pdata = fdata_new (parent, FALSE);
+            }
+            g_assert (pdata);
+            if (!port_add (&pdata->fobj, &pdata->len, pdata)) {
+                fdata_adjust_deleted (pdata);
+            }
+        } else {
+            /* f is root */
+            g_assert (IS_TOPNODE(FN_NODE(f)));
+            missing_add (f);
+        }
+    } else {
+#ifdef GIO_COMPILATION
+        pending_remove_node (FN_NODE(f), &op);
+#else
+        remove_node (FN_NODE(f), &op);
+#endif
+    }
+}
+
+static gboolean
+fdata_adjust_changed (fdata *f)
+{
+    fnode_event_t *ev;
+    struct stat buf;
+    node_t* parent;
+    fdata* pdata;
+
+    G_LOCK (fen_lock);
+    parent = get_parent_node (f);
+    pdata = get_parent_data (f);
+
+    if (!FN_IS_LIVING(f) ||
+      (children_num (FN_NODE(f)) == 0 &&
+        FN_IS_PASSIVE(f) &&
+        pdata && FN_IS_PASSIVE(pdata))) {
+        f->change_update_id = 0;
+        G_UNLOCK (fen_lock);
+        return FALSE;
+    }
+
+    FD_W ("[ %s ] %s\n", __func__, FN_NAME(f));
+    if (FN_STAT (FN_NAME(f), &buf) != 0) {
+        FD_W ("LSTAT [%-20s] %s\n", FN_NAME(f), g_strerror (errno));
+        goto L_delete;
+    }
+    f->is_dir = S_ISDIR (buf.st_mode) ? TRUE : FALSE;
+    if (f->len != buf.st_size) {
+        FD_W ("LEN [%lld:%lld] %s\n", f->len, buf.st_size, FN_NAME(f));
+        f->len = buf.st_size;
+        ev = fnode_event_new (FILE_MODIFIED, TRUE, f);
+        if (ev != NULL) {
+            ev->is_pending = TRUE;
+            fdata_add_event (f, ev);
+        }
+        /* Fdata is still changing, so scalable scan */
+        f->change_update_id = g_timeout_add (get_scalable_scan_time (f),
+          (GSourceFunc)fdata_adjust_changed,
+          (gpointer)f);
+        G_UNLOCK (fen_lock);
+        return FALSE;
+    } else {
+        f->changed_event_num = 0;
+        f->fobj.fo_atime = buf.st_atim;
+        f->fobj.fo_mtime = buf.st_mtim;
+        f->fobj.fo_ctime = buf.st_ctim;
+        if (FN_IS_DIR(f)) {
+            if (FN_IS_MONDIR(f)) {
+                scan_children (FN_NODE(f));
+            } else {
+                scan_known_children (FN_NODE(f));
+                if ((children_num (FN_NODE(f)) == 0 &&
+                      FN_IS_PASSIVE(f) &&
+                      pdata && FN_IS_PASSIVE(pdata))) {
+                    port_remove (f);
+                    goto L_exit;
+                }
+            }
+        }
+        if (!port_add_simple (&f->fobj, f)) {
+        L_delete:
+            ev = fnode_event_new (FILE_DELETE, FALSE, f);
+            if (ev != NULL) {
+                fdata_add_event (f, ev);
+            }
+        }
+    }
+L_exit:
+    f->change_update_id = 0;
+    G_UNLOCK (fen_lock);
+    return FALSE;
+}
+
+void
+fdata_emit_events_once (fdata *f, int event, gpointer sub)
+{
+    emit_once_cb (f, _event_converter (event), sub);
+}
+
+void
+fdata_emit_events (fdata *f, int event)
+{
+    emit_cb (f, _event_converter (event));
+}
+
+static gboolean
+process_events (gpointer udata)
+{
+    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
+    fdata* f;
+    fnode_event_t* ev;
+    int e;
+
+    /* FD_W ("IN <======== %s\n", __func__); */
+
+    f = (fdata*)udata;
+    FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
+    
+    G_LOCK (fen_lock);
+
+    if (!FN_IS_LIVING(f)) {
+        f->eventq_id = 0;
+        G_UNLOCK (fen_lock);
+        return FALSE;
+    }
+    
+    if ((ev = (fnode_event_t*)g_queue_pop_head (f->eventq)) != NULL) {
+        /* Send events to clients. */
+        e = ev->e;
+        if (!ev->is_pending) {
+#ifdef GIO_COMPILATION
+            if (ev->has_twin) {
+                fdata_emit_events (f, FILE_ATTRIB);
+            }
+#endif
+            fdata_emit_events (f, ev->e);
+        }
+        
+        fnode_event_delete (ev);
+        ev = NULL;
+
+        /* Adjust node state. */
+        /*
+         * Node the node has been created, so we can delete create event in
+         * optimizing. To reduce the statings, we add it to Port on discoving
+         * it then emit CREATED event. So we don't need to do anything here.
+         */
+        switch (e) {
+        case FILE_MODIFIED:
+        case MOUNTEDOVER:
+        case UNMOUNTED:
+            /* If the event is a changed event, then pending process it */
+            if (f->change_update_id == 0) {
+                f->change_update_id = g_timeout_add (get_scalable_scan_time(f),
+                  (GSourceFunc)fdata_adjust_changed,
+                  (gpointer)f);
+                g_assert (f->change_update_id > 0);
+            }
+            break;
+        case FILE_ATTRIB: /* Ignored */
+        case FILE_DELETE:
+            break;
+        default:
+            g_assert_not_reached ();
+            break;
+        }
+        /* Process one event a time */
+        G_UNLOCK (fen_lock); 
+        return TRUE;
+    }
+    f->eventq_id = 0;
+    G_UNLOCK (fen_lock); 
+    /* FD_W ("OUT ========> %s\n", __func__); */
+    return FALSE;
+}
+
+/**
+ * fdata_add_event:
+ *
+ */
+void
+fdata_add_event (fdata *f, fnode_event_t *ev)
+{
+    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
+    fnode_event_t *tail;
+
+    if (!FN_IS_LIVING(f)) {
+        fnode_event_delete (ev);
+        return;
+    }
+    
+    FD_W ("%s %d\n", __func__, ev->e);
+    g_get_current_time (&ev->t);
+    /*
+     * If created/deleted events of child node happened, then we use parent
+     * event queue to handle.
+     * If child node emits deleted event, it seems no changes for the parent
+     * node, but the attr is changed. So we may try to cancel processing the
+     * coming changed events of the parent node.
+     */
+    tail = (fnode_event_t*)g_queue_peek_tail (f->eventq);
+    switch (ev->e) {
+    case FILE_RENAME_FROM:
+    case FILE_RENAME_TO:
+    case FILE_ACCESS:
+        fnode_event_delete (ev);
+        g_assert_not_reached ();
+        return;
+    case FILE_DELETE:
+        /* clear changed event number */
+        f->changed_event_num = 0;
+        /*
+         * We will cancel all previous events.
+         */
+        if (tail) {
+            g_queue_pop_tail (f->eventq);
+            do {
+                fnode_event_delete (tail);
+            } while ((tail = (fnode_event_t*)g_queue_pop_tail (f->eventq)) != NULL);
+        }
+        /*
+         * Given a node "f" is deleted, process it ASAP.
+         */
+        fdata_emit_events (f, ev->e);
+        fnode_event_delete (ev);
+        fdata_adjust_deleted (f);
+        return;
+    case FILE_MODIFIED:
+    case UNMOUNTED:
+    case MOUNTEDOVER:
+        /* clear changed event number */
+        f->changed_event_num ++;
+    case FILE_ATTRIB:
+    default:
+        /*
+         * If in the time range, we will try optimizing
+         * (changed+) to (changed)
+         * (attrchanged changed) to ([changed, attrchanged])
+         * (event attrchanged) to ([event, attrchanged])
+         */
+        if (tail) {
+            do {
+                if (tail->e == ev->e) {
+                    if (g_timeval_lt (&ev->t, &tail->t)) {
+                        g_queue_peek_tail (f->eventq);
+                        /* Add the increment */
+                        g_time_val_add (&ev->t, PAIR_EVENTS_INC_TIMEVAL);
+                        /* skip the previous event */
+                        FD_W ("SKIPPED -- %s\n", _event_string (tail->e));
+                        fnode_event_delete (tail);
+                    } else {
+                        break;
+                    }
+                } else if (tail->e == FILE_ATTRIB && ev->e == FILE_MODIFIED) {
+                    ev->has_twin = TRUE;
+                    fnode_event_delete (tail);
+                } else if (ev->e == FILE_ATTRIB) {
+                    tail->has_twin = TRUE;
+                    /* skip the current event */
+                    fnode_event_delete (ev);
+                    return;
+                } else {
+                    break;
+                }
+            } while ((tail = (fnode_event_t*)g_queue_peek_tail (f->eventq)) != NULL);
+        }
+    }
+
+    /* must add the threshold time */
+    g_time_val_add (&ev->t, PAIR_EVENTS_TIMEVAL);
+    
+    g_queue_push_tail (f->eventq, ev);
+
+    /* starting process_events */
+    if (f->eventq_id == 0) {
+        f->eventq_id = g_timeout_add (PROCESS_EVENTQ_TIME,
+          process_events,
+          (gpointer)f);
+        g_assert (f->eventq_id > 0);
+    }
+    FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
+}
+
+gboolean
+fdata_class_init (void (*user_emit_cb) (fdata*, int),
+  void (*user_emit_once_cb) (fdata*, int,  gpointer),
+  int (*user_event_converter) (int event))
+{
+    FD_W ("%s\n", __func__);
+    if (user_emit_cb == NULL) {
+        return FALSE;
+    }
+    if (user_emit_once_cb == NULL) {
+        return FALSE;
+    }
+    if (user_event_converter == NULL) {
+        return FALSE;
+    }
+    emit_cb = user_emit_cb;
+    emit_once_cb = user_emit_once_cb;
+    _event_converter = user_event_converter;
+    
+    if (!port_class_init (fdata_add_event)) {
+        FD_W ("port_class_init failed.");
+        return FALSE;
+    }
+    return TRUE;
+}

Added: trunk/gio/fen/fen-data.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-data.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "fen-node.h"
+#include "fen-kernel.h"
+
+#ifndef _FEN_DATA_H_
+#define _FEN_DATA_H_
+
+#define FN_EVENT_CREATED	0
+#define FN_NAME(fp)	(((fdata*)(fp))->fobj.fo_name)
+#define FN_NODE(fp)	(((fdata*)(fp))->node)
+#define	FN_IS_DIR(fp)	(((fdata*)(fp))->is_dir)
+#define	FN_IS_PASSIVE(fp)	(((fdata*)(fp))->subs == NULL)
+#define	FN_IS_MONDIR(fp)	(((fdata*)(fp))->mon_dir_num > 0)
+#define	FN_IS_LIVING(fp)	(!((fdata*)(fp))->is_cancelled)
+
+typedef struct
+{
+	file_obj_t fobj;
+    off_t len;
+    gboolean is_cancelled;
+
+    node_t* node;
+	/* to identify if the path is dir */
+	gboolean is_dir;
+    guint mon_dir_num;
+
+	/* List of subscriptions monitoring this fdata/path */
+	GList *subs;
+
+    /* prcessed changed events num */
+    guint changed_event_num;
+    
+    /* process events source id */
+    GQueue* eventq;
+    guint eventq_id;
+    guint change_update_id;
+} fdata;
+
+/* fdata functions */
+fdata* fdata_new (node_t* node, gboolean is_mondir);
+void fdata_reset (fdata* data);
+void fdata_emit_events_once (fdata *f, int event, gpointer sub);
+void fdata_emit_events (fdata *f, int event);
+void fdata_add_event (fdata *f, fnode_event_t *ev);
+void fdata_adjust_deleted (fdata *f);
+fdata* get_parent_data (fdata* data);
+node_t* get_parent_node (fdata* data);
+gboolean is_monitoring (fdata* data);
+
+/* sub */
+void fdata_sub_add (fdata *f, gpointer sub);
+void fdata_sub_remove (fdata *f, gpointer sub);
+
+/* misc */
+node_t* add_missing_cb (node_t* parent, gpointer user_data);
+gboolean pre_del_cb (node_t* node, gpointer user_data);
+
+/* init */
+gboolean fdata_class_init (void (*user_emit_cb) (fdata*, int),
+  void (*user_emit_once_cb) (fdata*, int,  gpointer),
+  int (*user_event_converter) (int event));
+
+#endif /* _FEN_DATA_H_ */

Added: trunk/gio/fen/fen-dump.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-dump.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "config.h"
+#include <glib.h>
+#include <glib/gprintf.h>
+#include "fen-node.h"
+#include "fen-data.h"
+#include "fen-kernel.h"
+#include "fen-missing.h"
+#include "fen-dump.h"
+
+G_LOCK_EXTERN (fen_lock);
+
+/*-------------------- node ------------------*/
+static void
+dump_node (node_t* node, gpointer data)
+{
+    if (data && node->user_data) {
+        return;
+    }
+    g_printf ("[%s] < 0x%p : 0x%p > %s\n", __func__, node, node->user_data, NODE_NAME(node));
+}
+
+static gboolean
+dump_node_tree (node_t* node, gpointer user_data)
+{
+    node_op_t op = {dump_node, NULL, NULL, user_data};
+    GList* children;
+    GList* i;
+    if (G_TRYLOCK (fen_lock)) {
+        if (node) {
+            travel_nodes (node, &op);
+        }
+        G_UNLOCK (fen_lock);
+    }
+    return TRUE;
+}
+
+/* ------------------ fdata port hash --------------------*/
+void
+dump_hash_cb (gpointer key,
+  gpointer value,
+  gpointer user_data)
+{
+    g_printf ("[%s] < 0x%p : 0x%p >\n", __func__, key, value);
+}
+
+gboolean
+dump_hash (GHashTable* hash, gpointer user_data)
+{
+    if (G_TRYLOCK (fen_lock)) {
+        if (g_hash_table_size (hash) > 0) {
+            g_hash_table_foreach (hash, dump_hash_cb, user_data);
+        }
+        G_UNLOCK (fen_lock);
+    }
+    return TRUE;
+}
+
+/* ------------------ event --------------------*/
+void
+dump_event (fnode_event_t* ev, gpointer user_data)
+{
+    fdata* data = ev->user_data;
+    g_printf ("[%s] < 0x%p : 0x%p > [ %10s ] %s\n", __func__, ev, ev->user_data, _event_string (ev->e), FN_NAME(data));
+}
+
+void
+dump_event_queue (fdata* data, gpointer user_data)
+{
+    if (G_TRYLOCK (fen_lock)) {
+        if (data->eventq) {
+            g_queue_foreach (data->eventq, (GFunc)dump_event, user_data);
+        }
+        G_UNLOCK (fen_lock);
+    }
+}
+

Added: trunk/gio/fen/fen-dump.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-dump.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#ifndef _FEN_DUMP_H_
+#define _FEN_DUMP_H_
+
+
+#endif /* _FEN_DUMP_H_ */

Added: trunk/gio/fen/fen-helper.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-helper.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,330 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "config.h"
+#include <glib.h>
+#include "fen-data.h"
+#include "fen-helper.h"
+#include "fen-kernel.h"
+#ifdef GIO_COMPILATION
+#include "gfilemonitor.h"
+#else
+#include "gam_event.h"
+#include "gam_server.h"
+#include "gam_protocol.h"
+#endif
+
+#define FH_W if (fh_debug_enabled) g_warning
+static gboolean fh_debug_enabled = FALSE;
+
+G_LOCK_EXTERN (fen_lock);
+
+static void default_emit_event_cb (fdata *f, int events);
+static void default_emit_once_event_cb (fdata *f, int events, gpointer sub);
+static int default_event_converter (int event);
+
+static void
+scan_children_init (node_t *f, gpointer sub)
+{
+	GDir *dir;
+	GError *err = NULL;
+    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
+    fdata* pdata;
+    
+    FH_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
+    pdata = node_get_data (f);
+
+    dir = g_dir_open (NODE_NAME(f), 0, &err);
+    if (dir) {
+        const char *basename;
+        
+        while ((basename = g_dir_read_name (dir)))
+        {
+            node_t *childf = NULL;
+            fdata* data;
+            GList *idx;
+
+            childf = children_find (f, basename);
+            if (childf == NULL) {
+                gchar *filename;
+            
+                filename = g_build_filename (NODE_NAME(f), basename, NULL);
+                childf = add_node (f, filename);
+                g_assert (childf);
+                g_free (filename);
+            }
+            if ((data = node_get_data (childf)) == NULL) {
+                data = fdata_new (childf, FALSE);
+            }
+            
+            if (is_monitoring (data)) {
+                /* Ignored */
+            } else if (/* !is_ported (data) && */
+                port_add (&data->fobj, &data->len, data)) {
+                /* Emit created to all other subs */
+                fdata_emit_events (data, FN_EVENT_CREATED);
+            }
+            /* Emit created to the new sub */
+#ifdef GIO_COMPILATION
+            /* fdata_emit_events_once (data, FN_EVENT_CREATED, sub); */
+#else
+            gam_server_emit_one_event (NODE_NAME(childf),
+              gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1);
+#endif
+        }
+        g_dir_close (dir);
+    } else {
+        FH_W (err->message);
+        g_error_free (err);
+    }
+}
+
+/**
+ * fen_add
+ * 
+ * Won't hold a ref, we have a timout callback to clean unused fdata.
+ * If there is no value for a key, add it and return it; else return the old
+ * one.
+ */
+void
+fen_add (const gchar *filename, gpointer sub, gboolean is_mondir)
+{
+    node_op_t op = {NULL, add_missing_cb, pre_del_cb, (gpointer)filename};
+	node_t* f;
+    fdata* data;
+    
+    g_assert (filename);
+    g_assert (sub);
+
+    G_LOCK (fen_lock);
+	f = find_node_full (filename, &op);
+    FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename);
+    g_assert (f);
+    data = node_get_data (f);
+    if (data == NULL) {
+        data = fdata_new (f, is_mondir);
+    }
+
+    if (is_mondir) {
+        data->mon_dir_num ++;
+    }
+    
+    /* Change to active */
+#ifdef GIO_COMPILATION
+    if (port_add (&data->fobj, &data->len, data) ||
+      g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) {
+        if (is_mondir) {
+            scan_children_init (f, sub);
+        }
+        fdata_sub_add (data, sub);
+    } else {
+        fdata_sub_add (data, sub);
+        fdata_adjust_deleted (data);
+    }
+#else
+    if (port_add (&data->fobj, &data->len, data) ||
+      g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) {
+        gam_server_emit_one_event (FN_NAME(data),
+          gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1);
+        if (is_mondir) {
+            scan_children_init (f, sub);
+        }
+        gam_server_emit_one_event (FN_NAME(data),
+          gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1);
+        fdata_sub_add (data, sub);
+    } else {
+        fdata_sub_add (data, sub);
+        gam_server_emit_one_event (FN_NAME(data),
+          gam_subscription_is_dir (sub), GAMIN_EVENT_DELETED, sub, 1);
+        fdata_adjust_deleted (data);
+        gam_server_emit_one_event (FN_NAME(data),
+          gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1);
+    }
+#endif
+    G_UNLOCK (fen_lock);
+}
+
+void
+fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir)
+{
+    node_op_t op = {NULL, add_missing_cb, pre_del_cb, (gpointer)filename};
+    node_t* f;
+    fdata* data;
+    
+    g_assert (filename);
+    g_assert (sub);
+
+    G_LOCK (fen_lock);
+	f = find_node (filename);
+    FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename);
+
+    g_assert (f);
+    data = node_get_data (f);
+    g_assert (data);
+    
+    if (is_mondir) {
+        data->mon_dir_num --;
+    }
+    fdata_sub_remove (data, sub);
+    if (FN_IS_PASSIVE(data)) {
+#ifdef GIO_COMPILATION
+        pending_remove_node (f, &op);
+#else
+        remove_node (f, &op);
+#endif
+    }
+    G_UNLOCK (fen_lock);
+}
+
+static gboolean
+fen_init_once_func (gpointer data)
+{
+    FH_W ("%s\n", __func__);
+    if (!node_class_init ()) {
+        FH_W ("node_class_init failed.");
+        return FALSE;
+    }
+    if (!fdata_class_init (default_emit_event_cb,
+          default_emit_once_event_cb,
+          default_event_converter)) {
+        FH_W ("fdata_class_init failed.");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+gboolean
+fen_init ()
+{
+#ifdef GIO_COMPILATION
+    static GOnce fen_init_once = G_ONCE_INIT;
+    g_once (&fen_init_once, (GThreadFunc)fen_init_once_func, NULL);
+    return (gboolean)fen_init_once.retval;
+#else
+    return fen_init_once_func (NULL);
+#endif
+}
+
+static void
+default_emit_once_event_cb (fdata *f, int events, gpointer sub)
+{
+#ifdef GIO_COMPILATION
+    GFile* child;
+    fen_sub* _sub = (fen_sub*)sub;
+    child = g_file_new_for_path (FN_NAME(f));
+    g_file_monitor_emit_event (G_FILE_MONITOR (_sub->user_data),
+      child, NULL, events);
+    g_object_unref (child);
+#else
+    gam_server_emit_one_event (FN_NAME(f),
+      gam_subscription_is_dir (sub), events, sub, 1);
+#endif
+}
+
+static void
+default_emit_event_cb (fdata *f, int events)
+{
+    GList* i;
+    fdata* pdata;
+    
+#ifdef GIO_COMPILATION
+    GFile* child;
+    child = g_file_new_for_path (FN_NAME(f));
+    for (i = f->subs; i; i = i->next) {
+        fen_sub* sub = (fen_sub*)i->data;
+        gboolean file_is_dir = sub->is_mondir;
+        if ((events != G_FILE_MONITOR_EVENT_CHANGED &&
+              events != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) ||
+          !file_is_dir) {
+            g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
+              child, NULL, events);
+        }
+    }
+    if ((pdata = get_parent_data (f)) != NULL) {
+        for (i = pdata->subs; i; i = i->next) {
+            fen_sub* sub = (fen_sub*)i->data;
+            gboolean file_is_dir = sub->is_mondir;
+            g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
+              child, NULL, events);
+        }
+    }
+    g_object_unref (child);
+#else
+    for (i = f->subs; i; i = i->next) {
+        gboolean file_is_dir = gam_subscription_is_dir (i->data);
+        if (events != GAMIN_EVENT_CHANGED || !file_is_dir) {
+            gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1);
+        }
+    }
+    if ((pdata = get_parent_data (f)) != NULL) {
+        for (i = pdata->subs; i; i = i->next) {
+            gboolean file_is_dir = gam_subscription_is_dir (i->data);
+            gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1);
+        }
+    }
+#endif
+}
+
+static int
+default_event_converter (int event)
+{
+#ifdef GIO_COMPILATION
+    switch (event) {
+    case FN_EVENT_CREATED:
+        return G_FILE_MONITOR_EVENT_CREATED;
+    case FILE_DELETE:
+    case FILE_RENAME_FROM:
+        return G_FILE_MONITOR_EVENT_DELETED;
+    case UNMOUNTED:
+        return G_FILE_MONITOR_EVENT_UNMOUNTED;
+    case FILE_ATTRIB:
+        return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
+    case MOUNTEDOVER:
+    case FILE_MODIFIED:
+    case FILE_RENAME_TO:
+        return G_FILE_MONITOR_EVENT_CHANGED;
+    default:
+        /* case FILE_ACCESS: */
+        g_assert_not_reached ();
+        return -1;
+    }
+#else
+    switch (event) {
+    case FN_EVENT_CREATED:
+        return GAMIN_EVENT_CREATED;
+    case FILE_DELETE:
+    case FILE_RENAME_FROM:
+        return GAMIN_EVENT_DELETED;
+    case FILE_ATTRIB:
+    case MOUNTEDOVER:
+    case UNMOUNTED:
+    case FILE_MODIFIED:
+    case FILE_RENAME_TO:
+        return GAMIN_EVENT_CHANGED;
+    default:
+        /* case FILE_ACCESS: */
+        g_assert_not_reached ();
+        return -1;
+    }
+#endif
+}

Added: trunk/gio/fen/fen-helper.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-helper.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "fen-sub.h"
+
+#ifndef _FEN_HELPER_H_
+#define _FEN_HELPER_H_
+
+void fen_add (const gchar *filename, gpointer sub, gboolean is_mondir);
+void fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir);
+
+/* FEN subsystem initializing */
+gboolean fen_init ();
+
+#endif /* _FEN_HELPER_H_ */

Added: trunk/gio/fen/fen-kernel.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-kernel.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,523 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "config.h"
+#include <rctl.h>
+#include <strings.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <glib.h>
+#include "fen-kernel.h"
+#include "fen-dump.h"
+
+#define FK_W if (fk_debug_enabled) g_warning
+static gboolean fk_debug_enabled = FALSE;
+
+G_GNUC_INTERNAL G_LOCK_DEFINE (fen_lock);
+#define PE_ALLOC	64
+#define F_PORT(pp)		(((_f *)(fo))->port->port)
+#define F_NAME(pp)		(((_f *)(fo))->fobj->fo_name)
+#define FEN_ALL_EVENTS	(FILE_MODIFIED | FILE_ATTRIB | FILE_NOFOLLOW)
+#define FEN_IGNORE_EVENTS	(FILE_ACCESS)
+#define PROCESS_PORT_EVENTS_TIME	400	/* in milliseconds */
+
+static GHashTable *_obj_fen_hash = NULL;	/* <user_data, port> */
+static ulong max_port_events = 512;
+static GList *pn_vq;	/* the queue of ports which don't have the max objs */
+static GList *pn_fq;	/* the queue of ports which have the max objs */
+static GQueue *g_eventq = NULL;
+static void (*add_event_cb) (gpointer, fnode_event_t*);
+
+typedef struct pnode
+{
+	long ref;	/* how many fds are associated to this port */
+	int port;
+    guint port_source_id;
+} pnode_t;
+
+typedef struct {
+    pnode_t*	port;
+    file_obj_t*	fobj;
+
+    gboolean	is_active;
+    gpointer	user_data;
+} _f;
+
+static gboolean port_fetch_event_cb (void *arg);
+static pnode_t *pnode_new ();
+static void pnode_delete (pnode_t *pn);
+
+gboolean
+is_ported (gpointer f)
+{
+    _f* fo = g_hash_table_lookup (_obj_fen_hash, f);
+    
+    if (fo) {
+        return fo->is_active;
+    }
+    return FALSE;
+}
+
+static void
+printevent (const char *pname, int event, const char *tag)
+{
+    GString* str;
+    str = g_string_new ("");
+    
+    g_string_printf (str, "[%s] [%-20s]", tag, pname);
+    if (event & FILE_ACCESS) {
+        str = g_string_append (str, " ACCESS");
+    }
+    if (event & FILE_MODIFIED) {
+        str = g_string_append (str, " MODIFIED");
+    }
+    if (event & FILE_ATTRIB) {
+        str = g_string_append (str, " ATTRIB");
+    }
+    if (event & FILE_DELETE) {
+        str = g_string_append (str, " DELETE");
+    }
+    if (event & FILE_RENAME_TO) {
+        str = g_string_append (str, " RENAME_TO");
+    }
+    if (event & FILE_RENAME_FROM) {
+        str = g_string_append (str, " RENAME_FROM");
+    }
+    if (event & UNMOUNTED) {
+        str = g_string_append (str, " UNMOUNTED");
+    }
+    if (event & MOUNTEDOVER) {
+        str = g_string_append (str, " MOUNTEDOVER");
+    }
+
+    FK_W ("%s\n", str->str);
+    g_string_free (str, TRUE);
+}
+
+static void
+port_add_kevent (int e, gpointer f)
+{
+    fnode_event_t *ev, *tail;
+    GTimeVal t;
+    gboolean has_twin = FALSE;
+    
+    /* printevent (F_NAME(f), e, "org"); */
+    /*
+     * Child FILE_DELETE | FILE_RENAME_FROM will trigger parent FILE_MODIFIED.
+     * FILE_MODIFIED will trigger FILE_ATTRIB.
+     */
+
+    if ((e & FILE_ATTRIB) && e != FILE_ATTRIB) {
+        e ^= FILE_ATTRIB;
+        has_twin = TRUE;
+    }
+    if (e == FILE_RENAME_FROM) {
+        e = FILE_DELETE;
+    }
+    if (e == FILE_RENAME_TO) {
+        e = FILE_MODIFIED;
+    }
+    
+    switch (e) {
+    case FILE_DELETE:
+    case FILE_RENAME_FROM:
+    case FILE_MODIFIED:
+    case FILE_ATTRIB:
+    case UNMOUNTED:
+    case MOUNTEDOVER:
+        break;
+    case FILE_RENAME_TO:
+    case FILE_ACCESS:
+    default:
+        g_assert_not_reached ();
+        return;
+    }
+
+    tail = (fnode_event_t*) g_queue_peek_tail (g_eventq);
+    if (tail) {
+        if (tail->user_data == f) {
+            if (tail->e == e) {
+                tail->has_twin = (has_twin | (tail->has_twin ^ has_twin));
+                /* skip the current */
+                return;
+            } else if (e == FILE_MODIFIED && !has_twin
+              && tail->e == FILE_ATTRIB) {
+                tail->e = FILE_MODIFIED;
+                tail->has_twin = TRUE;
+                return;
+            } else if (e == FILE_ATTRIB
+              && tail->e == FILE_MODIFIED && !tail->has_twin) {
+                tail->has_twin = TRUE;
+                return;
+            }
+        }
+    }
+    
+    if ((ev = fnode_event_new (e, has_twin, f)) != NULL) {
+        g_queue_push_tail (g_eventq, ev);
+    }
+}
+
+static void
+port_process_kevents ()
+{
+    fnode_event_t *ev;
+    
+    while ((ev = (fnode_event_t*)g_queue_pop_head (g_eventq)) != NULL) {
+        FK_W ("[%s] 0x%p %s\n", __func__, ev, _event_string (ev->e));
+        add_event_cb (ev->user_data, ev);
+    }
+}
+
+static gboolean
+port_fetch_event_cb (void *arg)
+{
+	pnode_t *pn = (pnode_t *)arg;
+    _f* fo;
+	uint_t nget = 0;
+	port_event_t pe[PE_ALLOC];
+    timespec_t timeout;
+    gpointer f;
+    gboolean ret = TRUE;
+    
+    /* FK_W ("IN <======== %s\n", __func__); */
+    G_LOCK (fen_lock);
+    
+    memset (&timeout, 0, sizeof (timespec_t));
+    do {
+        nget = 1;
+        if (port_getn (pn->port, pe, PE_ALLOC, &nget, &timeout) == 0) {
+            int i;
+            for (i = 0; i < nget; i++) {
+                fo = (_f*)pe[i].portev_user;
+                /* handle event */
+                switch (pe[i].portev_source) {
+                case PORT_SOURCE_FILE:
+                    /* If got FILE_EXCEPTION or add to port failed,
+                       delete the pnode */
+                    fo->is_active = FALSE;
+                    if (fo->user_data) {
+                        printevent (F_NAME(fo), pe[i].portev_events, "RAW");
+                        port_add_kevent (pe[i].portev_events, fo->user_data);
+                    } else {
+                        /* fnode is deleted */
+                        goto L_delete;
+                    }
+                    if (pe[i].portev_events & FILE_EXCEPTION) {
+                        g_hash_table_remove (_obj_fen_hash, fo->user_data);
+                    L_delete:
+                        FK_W ("[ FREE_FO ] [0x%p]\n", fo);
+                        pnode_delete (fo->port);
+                        g_free (fo);
+                    }
+                    break;
+                default:
+                    /* case PORT_SOURCE_TIMER: */
+                    FK_W ("[kernel] unknown portev_source %d\n", pe[i].portev_source);
+                }
+            }
+        } else {
+            FK_W ("[kernel] port_getn %s\n", g_strerror (errno));
+            nget = 0;
+        }
+    } while (nget == PE_ALLOC);
+
+	/* Processing g_eventq */
+    port_process_kevents ();
+    
+    if (pn->ref == 0) {
+        pn->port_source_id = 0;
+        ret = FALSE;
+    }
+    G_UNLOCK (fen_lock);
+    /* FK_W ("OUT ========> %s\n", __func__); */
+	return ret;
+}
+
+/*
+ * ref - 1 if remove a watching file succeeded.
+ */
+static void
+pnode_delete (pnode_t *pn)
+{
+    g_assert (pn->ref <= max_port_events);
+    
+	if (pn->ref == max_port_events) {
+        FK_W ("PORT : move to visible queue - [pn] 0x%p [ref] %d\n", pn, pn->ref);
+		pn_fq = g_list_remove (pn_fq, pn);
+		pn_vq = g_list_prepend (pn_vq, pn);
+	}
+	if ((-- pn->ref) == 0) {
+        /* Should dispatch the source */
+	}
+	FK_W ("%s [pn] 0x%p [ref] %d\n", __func__, pn, pn->ref);
+}
+
+/*
+ * malloc pnode_t and port_create, start thread at pnode_ref.
+ * if pnode_new succeeded, the pnode_t will never
+ * be freed. So pnode_t can be freed only in pnode_new.
+ * Note pnode_monitor_remove_all can also free pnode_t, but currently no one
+ * invork it.
+ */
+static pnode_t *
+pnode_new ()
+{
+	pnode_t *pn = NULL;
+
+	if (pn_vq) {
+		pn = (pnode_t*)pn_vq->data;
+        g_assert (pn->ref < max_port_events);
+	} else {
+		pn = g_new0 (pnode_t, 1);
+		if (pn != NULL) {
+            if ((pn->port = port_create ()) >= 0) {
+                g_assert (g_list_find (pn_vq, pn) == NULL);
+                pn_vq = g_list_prepend (pn_vq, pn);
+            } else {
+                FK_W ("PORT_CREATE %s\n", g_strerror (errno));
+                g_free (pn);
+                pn = NULL;
+			}
+		}
+	}
+	if (pn) {
+		FK_W ("%s [pn] 0x%p [ref] %d\n", __func__, pn, pn->ref);
+        pn->ref++;
+        if (pn->ref == max_port_events) {
+            FK_W ("PORT : move to full queue - [pn] 0x%p [ref] %d\n", pn, pn->ref);
+            pn_vq = g_list_remove (pn_vq, pn);
+            pn_fq = g_list_prepend (pn_fq, pn);
+            g_assert (g_list_find (pn_vq, pn) == NULL);
+        }
+        /* attach the source */
+        if (pn->port_source_id == 0) {
+            pn->port_source_id = g_timeout_add (PROCESS_PORT_EVENTS_TIME,
+              port_fetch_event_cb,
+              (void *)pn);
+            g_assert (pn->port_source_id > 0);
+        }
+	}
+
+	return pn;
+}
+
+/**
+ * port_add_internal
+ *
+ * < private >
+ * Unsafe, need lock fen_lock.
+ */
+static gboolean
+port_add_internal (file_obj_t* fobj, off_t* len,
+  gpointer f, gboolean need_stat)
+{
+    int ret;
+    struct stat buf;
+    _f* fo = NULL;
+
+    g_assert (f && fobj);
+    FK_W ("%s [0x%p] %s\n", __func__, f, fobj->fo_name);
+
+    if ((fo = g_hash_table_lookup (_obj_fen_hash, f)) == NULL) {
+        fo = g_new0 (_f, 1);
+        fo->fobj = fobj;
+        fo->user_data = f;
+        g_assert (fo);
+        FK_W ("[ NEW_FO ] [0x%p] %s\n", fo, F_NAME(fo));
+        g_hash_table_insert (_obj_fen_hash, f, fo);
+    }
+
+    if (fo->is_active) {
+        return TRUE;
+    }
+
+    if (fo->port == NULL) {
+        fo->port = pnode_new ();
+    }
+    
+    if (need_stat) {
+        if (FN_STAT (F_NAME(fo), &buf) != 0) {
+            FK_W ("LSTAT [%-20s] %s\n", F_NAME(fo), g_strerror (errno));
+            goto L_exit;
+        }
+        g_assert (len);
+        fo->fobj->fo_atime = buf.st_atim;
+        fo->fobj->fo_mtime = buf.st_mtim;
+        fo->fobj->fo_ctime = buf.st_ctim;
+        *len = buf.st_size;
+    }
+    
+    if (port_associate (F_PORT(fo),
+          PORT_SOURCE_FILE,
+          (uintptr_t)fo->fobj,
+          FEN_ALL_EVENTS,
+          (void *)fo) == 0) {
+        fo->is_active = TRUE;
+        FK_W ("%s %s\n", "PORT_ASSOCIATE", F_NAME(fo));
+        return TRUE;
+    } else {
+        FK_W ("PORT_ASSOCIATE [%-20s] %s\n", F_NAME(fo), g_strerror (errno));
+    L_exit:
+        FK_W ("[ FREE_FO ] [0x%p]\n", fo);
+        g_hash_table_remove (_obj_fen_hash, f);
+        pnode_delete (fo->port);
+        g_free (fo);
+    }
+    return FALSE;
+}
+
+gboolean
+port_add (file_obj_t* fobj, off_t* len, gpointer f)
+{
+    return port_add_internal (fobj, len, f, TRUE);
+}
+
+gboolean
+port_add_simple (file_obj_t* fobj, gpointer f)
+{
+    return port_add_internal (fobj, NULL, f, FALSE);
+}
+
+/**
+ * port_remove
+ *
+ * < private >
+ * Unsafe, need lock fen_lock.
+ */
+void
+port_remove (gpointer f)
+{
+    _f* fo = NULL;
+
+    FK_W ("%s\n", __func__);
+    if ((fo = g_hash_table_lookup (_obj_fen_hash, f)) != NULL) {
+        /* Marked */
+        fo->user_data = NULL;
+        g_hash_table_remove (_obj_fen_hash, f);
+        
+        if (port_dissociate (F_PORT(fo),
+              PORT_SOURCE_FILE,
+              (uintptr_t)fo->fobj) == 0) {
+            /*
+             * Note, we can run foode_delete if dissociating is failed,
+             * because there may be some pending events (mostly like
+             * FILE_DELETE) in the port_get. If we delete the foode
+             * the fnode may be deleted, then port_get will run on an invalid
+             * address.
+             */
+            FK_W ("[ FREE_FO ] [0x%p]\n", fo);
+            pnode_delete (fo->port);
+            g_free (fo);
+        } else {
+            FK_W ("PORT_DISSOCIATE [%-20s] %s\n", F_NAME(fo), g_strerror (errno));
+        }
+    }
+}
+
+const gchar *
+_event_string (int event)
+{
+    switch (event) {
+    case FILE_DELETE:
+        return "FILE_DELETE";
+    case FILE_RENAME_FROM:
+        return "FILE_RENAME_FROM";
+    case FILE_MODIFIED:
+        return "FILE_MODIFIED";
+    case FILE_RENAME_TO:
+        return "FILE_RENAME_TO";
+    case MOUNTEDOVER:
+        return "MOUNTEDOVER";
+    case FILE_ATTRIB:
+        return "FILE_ATTRIB";
+    case UNMOUNTED:
+        return "UNMOUNTED";
+    case FILE_ACCESS:
+        return "FILE_ACCESS";
+    default:
+        return "EVENT_UNKNOWN";
+    }
+}
+
+/**
+ * Get Solaris resouce values.
+ *
+ */
+
+extern gboolean
+port_class_init (void (*user_add_event) (gpointer, fnode_event_t*))
+{
+	rctlblk_t *rblk;
+    FK_W ("%s\n", __func__);
+	if ((rblk = malloc (rctlblk_size ())) == NULL) {
+        FK_W ("[kernel] rblk malloc %s\n", g_strerror (errno));
+		return FALSE;
+	}
+	if (getrctl ("process.max-port-events", NULL, rblk, RCTL_FIRST) == -1) {
+        FK_W ("[kernel] getrctl %s\n", g_strerror (errno));
+        free (rblk);
+        return FALSE;
+	} else {
+        max_port_events = rctlblk_get_value(rblk);
+		FK_W ("[kernel] max_port_events = %u\n", max_port_events);
+        free (rblk);
+	}
+    if ((_obj_fen_hash = g_hash_table_new(g_direct_hash,
+           g_direct_equal)) == NULL) {
+        FK_W ("[kernel] fobj hash initializing faild\n");
+        return FALSE;
+    }
+    if ((g_eventq = g_queue_new ()) == NULL) {
+		FK_W ("[kernel] FEN global event queue initializing faild\n");
+    }
+    if (user_add_event == NULL) {
+        return FALSE;
+    }
+    add_event_cb = user_add_event;
+	return TRUE;
+}
+
+fnode_event_t*
+fnode_event_new (int event, gboolean has_twin, gpointer user_data)
+{
+    fnode_event_t *ev;
+    
+    if ((ev = g_new (fnode_event_t, 1)) != NULL) {
+        g_assert (ev);
+        ev->e = event;
+        ev->user_data = user_data;
+        ev->has_twin = has_twin;
+        /* Default isn't a pending event. */
+        ev->is_pending = FALSE;
+    }
+    return ev;
+}
+
+void
+fnode_event_delete (fnode_event_t* ev)
+{
+    g_free (ev);
+}

Added: trunk/gio/fen/fen-kernel.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-kernel.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include <port.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef _FEN_KERNEL_H_
+#define _FEN_KERNEL_H_
+
+#define FN_STAT	lstat
+
+typedef struct fnode_event
+{
+    int e;
+    gboolean has_twin;
+    gboolean is_pending;
+    gpointer user_data;
+    GTimeVal t;
+} fnode_event_t;
+
+gboolean port_add (file_obj_t* fobj, off_t* len, gpointer f);
+gboolean port_add_simple (file_obj_t* fobj, gpointer f);
+void port_remove (gpointer f);
+gboolean is_ported (gpointer f);
+
+fnode_event_t* fnode_event_new (int event, gboolean has_twin, gpointer user_data);
+void fnode_event_delete (fnode_event_t* ev);
+const gchar * _event_string (int event);
+
+extern gboolean port_class_init ();
+
+#endif /* _FEN_KERNEL_H_ */

Added: trunk/gio/fen/fen-missing.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-missing.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,114 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "config.h"
+#include <glib.h>
+#include "fen-data.h"
+#include "fen-missing.h"
+
+G_LOCK_EXTERN (fen_lock);
+#define SCAN_MISSING_INTERVAL 4000	/* in milliseconds */
+#define FM_W if (fm_debug_enabled) g_warning
+
+/* global data structure for scan missing files */
+gboolean fm_debug_enabled = TRUE;
+static GList *missing_list = NULL;
+static guint scan_missing_source_id = 0;
+
+static gboolean scan_missing_list (gpointer data);
+
+static gboolean
+scan_missing_list (gpointer data)
+{
+    GList *existing_list = NULL;
+    GList *idx = NULL;
+    fdata *f;
+    gboolean ret = TRUE;
+
+    G_LOCK (fen_lock);
+    
+    for (idx = missing_list; idx; idx = idx->next) {
+        f = (fdata*)idx->data;
+        
+        if (port_add (&f->fobj, &f->len, f)) {
+            /* TODO - emit CREATE event */
+            fdata_emit_events (f, FN_EVENT_CREATED);
+            existing_list = g_list_prepend (existing_list, idx);
+        }
+    }
+    
+    for (idx = existing_list; idx; idx = idx->next) {
+        missing_list = g_list_remove_link (missing_list, (GList *)idx->data);
+        g_list_free_1 ((GList *)idx->data);
+    }
+    g_list_free (existing_list);
+
+    if (missing_list == NULL) {
+        scan_missing_source_id = 0;
+        ret = FALSE;
+    }
+
+    G_UNLOCK (fen_lock);
+    return ret;
+}
+
+/**
+ * missing_add
+ *
+ * Unsafe, need lock fen_lock.
+ */
+void
+missing_add (fdata *f)
+{
+    GList *idx;
+    
+    g_assert (!is_ported (f));
+
+    if (g_list_find (missing_list, f) != NULL) {
+        FM_W ("%s is ALREADY added %s\n", __func__, FN_NAME(f));
+        return;
+    }
+    FM_W ("%s is added %s\n", __func__, FN_NAME(f));
+    
+    missing_list = g_list_prepend (missing_list, f);
+    
+    /* if doesn't scan, then start */
+    if (scan_missing_source_id == 0) {
+        scan_missing_source_id = g_timeout_add (SCAN_MISSING_INTERVAL,
+          scan_missing_list,
+          NULL);
+        g_assert (scan_missing_source_id > 0);
+    }
+}
+
+/**
+ * missing_remove
+ *
+ * Unsafe, need lock fen_lock.
+ */
+void
+missing_remove (fdata *f)
+{
+    FM_W ("%s %s\n", __func__, FN_NAME(f));
+    missing_list = g_list_remove (missing_list, f);
+}

Added: trunk/gio/fen/fen-missing.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-missing.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#ifndef __GAM_FEN_H__
+#define __GAM_FEN_H__
+
+#include "fen-data.h"
+
+G_BEGIN_DECLS
+
+extern void missing_add (fdata *f);
+extern void missing_remove (fdata *f);
+
+G_END_DECLS
+
+#endif /* __GAM_FEN_H__ */
+

Added: trunk/gio/fen/fen-node.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-node.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,460 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <strings.h>
+#include <glib.h>
+#include "fen-node.h"
+#include "fen-dump.h"
+
+#define	NODE_STAT(n)	(((node_t*)(n))->stat)
+
+struct _dnode {
+    gchar* filename;
+    node_op_t* op;
+    GTimeVal tv;
+};
+
+#define FN_W if (fn_debug_enabled) g_warning
+static gboolean fn_debug_enabled = FALSE;
+
+G_LOCK_EXTERN (fen_lock);
+#define	PROCESS_DELETING_INTERVAL	900 /* in second */
+
+static node_t* _head = NULL;
+static GList *deleting_nodes = NULL;
+static guint deleting_nodes_id = 0;
+
+static node_t* node_new (node_t* parent, const gchar* basename);
+static void node_delete (node_t* parent);
+static gboolean remove_node_internal (node_t* node, node_op_t* op);
+static void children_add (node_t *p, node_t *f);
+static void children_remove (node_t *p, node_t *f);
+static guint children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data);
+static void children_foreach (node_t *f, GHFunc func, gpointer user_data);
+static gboolean children_remove_cb (gpointer key,
+  gpointer value,
+  gpointer user_data);
+
+static struct _dnode*
+_dnode_new (const gchar* filename, node_op_t* op)
+{
+    struct _dnode* d;
+
+    g_assert (op);
+    if ((d = g_new (struct _dnode, 1)) != NULL) {
+        d->filename = g_strdup (filename);
+        d->op = g_memdup (op, sizeof (node_op_t));
+        g_assert (d->op);
+        g_get_current_time (&d->tv);
+        g_time_val_add (&d->tv, PROCESS_DELETING_INTERVAL);
+    }
+    return d;
+}
+
+static void
+_dnode_free (struct _dnode* d)
+{
+    g_assert (d);
+    g_free (d->filename);
+    g_free (d->op);
+    g_free (d);
+}
+
+static gboolean
+g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
+{
+    if (val1->tv_sec < val2->tv_sec)
+        return TRUE;
+  
+    if (val1->tv_sec > val2->tv_sec)
+        return FALSE;
+  
+    /* val1->tv_sec == val2->tv_sec */
+    if (val1->tv_usec < val2->tv_usec)
+        return TRUE;
+  
+    return FALSE;
+}
+
+static gboolean
+scan_deleting_nodes (gpointer data)
+{
+    struct _dnode* d;
+    GTimeVal tv_now;
+    GList* i;
+    GList* deleted_list = NULL;
+    gboolean ret = TRUE;
+    node_t* node;
+
+    g_get_current_time (&tv_now);
+
+    if (G_TRYLOCK (fen_lock)) {
+        for (i = deleting_nodes; i; i = i->next) {
+            d = (struct _dnode*)i->data;
+            /* Time to free, try only once */
+            if (g_timeval_lt (&d->tv, &tv_now)) {
+                if ((node = find_node (d->filename)) != NULL) {
+                    remove_node_internal (node, d->op);
+                }
+                _dnode_free (d);
+                deleted_list = g_list_prepend (deleted_list, i);
+            }
+        }
+
+        for (i = deleted_list; i; i = i->next) {
+            deleting_nodes = g_list_remove_link (deleting_nodes,
+              (GList *)i->data);
+            g_list_free_1 ((GList *)i->data);
+        }
+        g_list_free (deleted_list);
+
+        if (deleting_nodes == NULL) {
+            deleting_nodes_id = 0;
+            ret = FALSE;
+        }
+        G_UNLOCK (fen_lock);
+    }
+    return ret;
+}
+
+gpointer
+node_get_data (node_t* node)
+{
+    g_assert (node);
+    return node->user_data;
+}
+
+gpointer
+node_set_data (node_t* node, gpointer user_data)
+{
+    gpointer data = node->user_data;
+    g_assert (node);
+    node->user_data = user_data;
+    return data;
+}
+
+void
+travel_nodes (node_t* node, node_op_t* op)
+{
+    GList* children;
+    GList* i;
+
+    if (node) {
+        if (op && op->hit) {
+            op->hit (node, op->user_data);
+        }
+    }
+    children = g_hash_table_get_values (node->children);
+    if (children) {
+        for (i = children; i; i = i->next) {
+            travel_nodes (i->data, op);
+        }
+        g_list_free (children);
+    }
+}
+
+static node_t*
+find_node_internal (node_t* node, const gchar* filename, node_op_t* op)
+{
+    gchar* str;
+    gchar* token;
+    gchar* lasts;
+    node_t* parent;
+    node_t* child;
+    
+    g_assert (filename && filename[0] == '/');
+    g_assert (node);
+    
+    parent = node;
+    str = g_strdup (filename + strlen (NODE_NAME(parent)));
+    
+    if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
+        do {
+            FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
+            child = children_find (parent, token);
+            if (child) {
+                parent = child;
+            } else {
+                if (op && op->add_missing) {
+                    child = op->add_missing (parent, op->user_data);
+                    goto L_hit;
+                }
+                break;
+            }
+        } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
+    } else {
+        /* It's the head */
+        g_assert (parent == _head);
+        child = _head;
+    }
+    
+    if (token == NULL && child) {
+    L_hit:
+        if (op && op->hit) {
+            op->hit (child, op->user_data);
+        }
+    }
+    g_free (str);
+    return child;
+}
+
+node_t*
+find_node (const gchar *filename)
+{
+    return find_node_internal (_head, filename, NULL);
+}
+
+node_t*
+find_node_full (const gchar* filename, node_op_t* op)
+{
+    return find_node_internal (_head, filename, op);
+}
+
+node_t*
+add_node (node_t* parent, const gchar* filename)
+{
+    gchar* str;
+    gchar* token;
+    gchar* lasts;
+    node_t* child = NULL;
+
+    g_assert (_head);
+    g_assert (filename && filename[0] == '/');
+
+    if (parent == NULL) {
+        parent = _head;
+    }
+    
+    str = g_strdup (filename + strlen (NODE_NAME(parent)));
+    
+    if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
+        do {
+            FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
+            child = node_new (parent, token);
+            if (child) {
+                children_add (parent, child);
+                parent = child;
+            } else {
+                break;
+            }
+        } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
+    }
+    g_free (str);
+    if (token == NULL) {
+        return child;
+    } else {
+        return NULL;
+    }
+}
+
+/**
+ * delete recursively
+ */
+static gboolean
+remove_children (node_t* node, node_op_t* op)
+{
+    FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
+    if (children_num (node) > 0) {
+        children_foreach_remove (node, children_remove_cb,
+          (gpointer)op);
+    }
+    if (children_num (node) == 0) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static gboolean
+remove_node_internal (node_t* node, node_op_t* op)
+{
+    node_t* parent = NULL;
+    /*
+     * If the parent is passive and doesn't have children, delete it.
+     * NOTE node_delete_deep is a depth first delete recursively.
+     * Top node is deleted in node_cancel_sub
+     */
+    g_assert (node);
+    g_assert (op && op->pre_del);
+    if (node != _head) {
+        if (remove_children (node, op)) {
+            if (node->user_data) {
+                if (!op->pre_del (node, op->user_data)) {
+                    return FALSE;
+                }
+            }
+            parent = node->parent;
+            children_remove (parent, node);
+            node_delete (node);
+            if (children_num (parent) == 0) {
+                remove_node_internal (parent, op);
+            }
+            return TRUE;
+        }
+        return FALSE;
+    }
+    return TRUE;
+}
+
+void
+pending_remove_node (node_t* node, node_op_t* op)
+{
+    struct _dnode* d;
+    GList* l;
+    
+    for (l = deleting_nodes; l; l=l->next) {
+        d = (struct _dnode*) l->data;
+        if (g_ascii_strcasecmp (d->filename, NODE_NAME(node)) == 0) {
+            return;
+        }
+    }
+    
+    d = _dnode_new (NODE_NAME(node), op);
+    g_assert (d);
+    deleting_nodes = g_list_prepend (deleting_nodes, d);
+    if (deleting_nodes_id == 0) {
+        deleting_nodes_id = g_timeout_add_seconds (PROCESS_DELETING_INTERVAL,
+          scan_deleting_nodes,
+          NULL);
+        g_assert (deleting_nodes_id > 0);
+    }
+}
+
+void
+remove_node (node_t* node, node_op_t* op)
+{
+    remove_node_internal (node, op);
+}
+
+static node_t*
+node_new (node_t* parent, const gchar* basename)
+{
+	node_t *f = NULL;
+
+    g_assert (basename && basename[0]);
+    if ((f = g_new0 (node_t, 1)) != NULL) {
+        if (parent) {
+            f->basename = g_strdup (basename);
+            f->filename = g_build_filename (G_DIR_SEPARATOR_S,
+              NODE_NAME(parent), basename, NULL);
+        } else {
+            f->basename = g_strdup (basename);
+            f->filename = g_strdup (basename);
+        }
+        f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
+          NULL, (GDestroyNotify)node_delete);
+        FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
+    }
+	return f;
+}
+
+static void
+node_delete (node_t *f)
+{
+    FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
+    g_assert (g_hash_table_size (f->children) == 0);
+    g_assert (f->user_data == NULL);
+
+    g_hash_table_unref (f->children);
+    g_free (f->basename);
+    g_free (f->filename);
+    g_free (f);
+}
+
+static void
+children_add (node_t *p, node_t *f)
+{
+    FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
+    g_hash_table_insert (p->children, f->basename, f);
+    f->parent = p;
+}
+
+static void
+children_remove (node_t *p, node_t *f)
+{
+    FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
+    g_hash_table_steal (p->children, f->basename);
+    f->parent = NULL;
+}
+
+guint
+children_num (node_t *f)
+{
+    return g_hash_table_size (f->children);
+}
+
+node_t *
+children_find (node_t *f, const gchar *basename)
+{
+    return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
+}
+
+/**
+ * depth first delete recursively
+ */
+static gboolean
+children_remove_cb (gpointer key,
+  gpointer value,
+  gpointer user_data)
+{
+    node_t* f = (node_t*)value;
+    node_op_t* op = (node_op_t*) user_data;
+    
+    g_assert (f->parent);
+
+    FN_W ("%s [p] %8s [c] %8s\n", __func__, f->parent->basename, f->basename);
+    if (remove_children (f, op)) {
+        if (f->user_data != NULL) {
+            return op->pre_del (f, op->user_data);
+        }
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static guint
+children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data)
+{
+    g_assert (f);
+    
+    return g_hash_table_foreach_remove (f->children, func, user_data);
+}
+
+static void
+children_foreach (node_t *f, GHFunc func, gpointer user_data)
+{
+    g_assert (f);
+    
+    g_hash_table_foreach (f->children, func, user_data);
+}
+
+gboolean
+node_class_init ()
+{
+    FN_W ("%s\n", __func__);
+    if (_head == NULL) {
+        _head = node_new (NULL, G_DIR_SEPARATOR_S);
+    }
+    return _head != NULL;
+}

Added: trunk/gio/fen/fen-node.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-node.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#ifndef _FEN_NODE_H_
+#define _FEN_NODE_H_
+
+typedef struct node node_t;
+
+struct node
+{
+    gchar *filename;
+    gchar *basename;
+    gint stat;
+    
+	/* the parent and children of node */
+    node_t *parent;
+    GHashTable *children; /* children in basename */
+
+    gpointer user_data;
+};
+
+#define	IS_TOPNODE(fp)	(((node_t *)(fp))->parent == NULL)
+#define NODE_NAME(fp)	(((node_t *)(fp))->filename)
+
+typedef struct node_op
+{
+    /* find */
+    void (*hit) (node_t* node, gpointer user_data);
+    node_t* (*add_missing) (node_t* parent, gpointer user_data);
+    /* delete */
+    gboolean (*pre_del) (node_t* node, gpointer user_data);
+	/* data */
+    gpointer user_data;
+} node_op_t;
+
+node_t* add_node (node_t* parent, const gchar* filename);
+void remove_node (node_t* node, node_op_t* op);
+void pending_remove_node (node_t* node, node_op_t* op);
+
+void travel_nodes (node_t* node, node_op_t* op);
+node_t* find_node_full (const gchar* filename, node_op_t* op);
+node_t* find_node (const gchar *filename);
+
+node_t* children_find (node_t *f, const gchar *basename);
+guint children_num (node_t *f);
+
+gpointer node_get_data (node_t* node);
+gpointer node_set_data (node_t* node, gpointer user_data);
+
+gboolean node_class_init ();
+
+#endif /* _FEN_NODE_H_ */

Added: trunk/gio/fen/fen-sub.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-sub.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include "config.h"
+#include "fen-sub.h"
+
+fen_sub*
+fen_sub_new (gpointer udata, gboolean is_mondir)
+{
+    fen_sub *sub;
+    sub = g_new (fen_sub, 1);
+    sub->user_data = udata;
+    sub->is_mondir = is_mondir;
+    return sub;
+}
+
+void
+fen_sub_delete (fen_sub *sub)
+{
+    g_free (sub);
+}

Added: trunk/gio/fen/fen-sub.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/fen-sub.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* 
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Lin Ma <lin ma sun com>
+ */
+
+#include <glib.h>
+
+#ifndef _FEN_SUB_H_
+#define _FEN_SUB_H_
+
+typedef struct _fen_sub
+{
+    gpointer user_data;
+    gboolean is_mondir;
+} fen_sub;
+
+fen_sub*	fen_sub_new (gpointer udata, gboolean is_mondir);
+void		fen_sub_delete (fen_sub *sub);
+
+#endif _FEN_SUB_H_

Added: trunk/gio/fen/gfendirectorymonitor.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/gfendirectorymonitor.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,149 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian DrÃge.
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl redhat com>
+ *          John McCutchan <john johnmccutchan com> 
+ *          Sebastian DrÃge <slomo circular-chaos org>
+ *          Lin Ma <lin ma sun com>
+ */
+
+#include <config.h>
+
+#include "gfendirectorymonitor.h"
+#include "giomodule.h"
+
+#include "fen-helper.h"
+
+#include "gioalias.h"
+
+struct _GFenDirectoryMonitor
+{
+	GLocalDirectoryMonitor parent_instance;
+    gboolean cancelled;
+    fen_sub* sub;
+};
+
+static gboolean g_fen_directory_monitor_cancel (GFileMonitor* monitor);
+
+#define g_fen_directory_monitor_get_type _g_fen_directory_monitor_get_type
+G_DEFINE_TYPE_WITH_CODE (GFenDirectoryMonitor, g_fen_directory_monitor, G_TYPE_LOCAL_DIRECTORY_MONITOR,
+  g_io_extension_point_implement (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME,
+    g_define_type_id,
+    "fen",
+    20))
+
+static void
+g_fen_directory_monitor_finalize (GObject *object)
+{
+	GFenDirectoryMonitor *self = G_FEN_DIRECTORY_MONITOR (object);
+    
+    if (self->sub) {
+        fen_remove (G_LOCAL_DIRECTORY_MONITOR (self)->dirname, self->sub, TRUE);
+        fen_sub_delete (self->sub);
+        self->sub = NULL;
+    }
+
+	if (G_OBJECT_CLASS (g_fen_directory_monitor_parent_class)->finalize)
+		(*G_OBJECT_CLASS (g_fen_directory_monitor_parent_class)->finalize) (object);
+}
+
+static GObject *
+g_fen_directory_monitor_constructor (GType type,
+  guint n_construct_properties,
+  GObjectConstructParam *construct_properties)
+{
+	GObject *obj;
+	GFenDirectoryMonitorClass *klass;
+	GObjectClass *parent_class;
+	GFenDirectoryMonitor *self;
+	const gchar *dirname = NULL;
+  
+	klass = G_FEN_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_FEN_DIRECTORY_MONITOR));
+	parent_class = g_fen_directory_monitor_parent_class;
+	obj = parent_class->constructor (type,
+      n_construct_properties,
+      construct_properties);
+
+	self = G_FEN_DIRECTORY_MONITOR (obj);
+
+	dirname = G_LOCAL_DIRECTORY_MONITOR (self)->dirname;
+	g_assert (dirname != NULL);
+
+	/* Will never fail as is_supported() should be called before instanciating
+	 * anyway */
+    if (!fen_init ())
+        g_assert_not_reached ();
+
+	/* FIXME: what to do about errors here? we can't return NULL or another
+	 * kind of error and an assertion is probably too hard */
+    self->sub = fen_sub_new (self, TRUE);
+    g_assert (self->sub);
+    
+    fen_add (dirname, self->sub, TRUE);
+
+	return obj;
+}
+
+static gboolean
+g_fen_directory_monitor_is_supported (void)
+{
+	return fen_init ();
+}
+
+static void
+g_fen_directory_monitor_class_init (GFenDirectoryMonitorClass* klass)
+{
+	GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+	GFileMonitorClass *directory_monitor_class = G_FILE_MONITOR_CLASS (klass);
+	GLocalDirectoryMonitorClass *local_directory_monitor_class = G_LOCAL_DIRECTORY_MONITOR_CLASS (klass);
+  
+	gobject_class->finalize = g_fen_directory_monitor_finalize;
+	gobject_class->constructor = g_fen_directory_monitor_constructor;
+	directory_monitor_class->cancel = g_fen_directory_monitor_cancel;
+
+	local_directory_monitor_class->mount_notify = TRUE;
+	local_directory_monitor_class->is_supported = g_fen_directory_monitor_is_supported;
+}
+
+static void
+g_fen_directory_monitor_init (GFenDirectoryMonitor* monitor)
+{
+}
+
+static gboolean
+g_fen_directory_monitor_cancel (GFileMonitor* monitor)
+{
+	GFenDirectoryMonitor *self = G_FEN_DIRECTORY_MONITOR (monitor);
+    
+    if (self->sub) {
+        fen_remove (G_LOCAL_DIRECTORY_MONITOR (self)->dirname, self->sub, TRUE);
+        fen_sub_delete (self->sub);
+        self->sub = NULL;
+    }
+    
+	if (G_FILE_MONITOR_CLASS (g_fen_directory_monitor_parent_class)->cancel)
+		(*G_FILE_MONITOR_CLASS (g_fen_directory_monitor_parent_class)->cancel) (monitor);
+
+	return TRUE;
+}
+

Added: trunk/gio/fen/gfendirectorymonitor.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/gfendirectorymonitor.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian DrÃge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl redhat com>
+ *          John McCutchan <john johnmccutchan com> 
+ *          Sebastian DrÃge <slomo circular-chaos org>
+ */
+
+#ifndef __G_FEN_DIRECTORY_MONITOR_H__
+#define __G_FEN_DIRECTORY_MONITOR_H__
+
+#include <glib-object.h>
+#include <string.h>
+#include "glocaldirectorymonitor.h"
+#include "giomodule.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FEN_DIRECTORY_MONITOR		(_g_fen_directory_monitor_get_type ())
+#define G_FEN_DIRECTORY_MONITOR(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FEN_DIRECTORY_MONITOR, GFenDirectoryMonitor))
+#define G_FEN_DIRECTORY_MONITOR_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_FEN_DIRECTORY_MONITOR, GFenDirectoryMonitorClass))
+#define G_IS_FEN_DIRECTORY_MONITOR(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FEN_DIRECTORY_MONITOR))
+#define G_IS_FEN_DIRECTORY_MONITOR_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FEN_DIRECTORY_MONITOR))
+
+typedef struct _GFenDirectoryMonitor      GFenDirectoryMonitor;
+typedef struct _GFenDirectoryMonitorClass GFenDirectoryMonitorClass;
+
+struct _GFenDirectoryMonitorClass {
+  GLocalDirectoryMonitorClass parent_class;
+};
+
+GType _g_fen_directory_monitor_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_FEN_DIRECTORY_MONITOR_H__ */

Added: trunk/gio/fen/gfenfilemonitor.c
==============================================================================
--- (empty file)
+++ trunk/gio/fen/gfenfilemonitor.c	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,148 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian DrÃge.
+ * Copyright (C) 2008 Sun Microsystem.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl redhat com>
+ *          John McCutchan <john johnmccutchan com> 
+ *          Sebastian DrÃge <slomo circular-chaos org>
+ *          Lin Ma <lin ma sun com>
+ */
+
+#include <config.h>
+
+#include "gfenfilemonitor.h"
+#include <gio/giomodule.h>
+
+#include "fen-helper.h"
+
+#include "gioalias.h"
+
+struct _GFenFileMonitor
+{
+    GLocalFileMonitor parent_instance;
+    fen_sub* sub;
+};
+
+static gboolean g_fen_file_monitor_cancel (GFileMonitor* monitor);
+
+#define g_fen_file_monitor_get_type _g_fen_file_monitor_get_type
+G_DEFINE_TYPE_WITH_CODE (GFenFileMonitor, g_fen_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
+  g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
+    g_define_type_id,
+    "fen",
+    20))
+
+static void
+g_fen_file_monitor_finalize (GObject *object)
+{
+	GFenFileMonitor *self = G_FEN_FILE_MONITOR (object);
+    
+    if (self->sub) {
+        fen_remove (G_LOCAL_FILE_MONITOR (self)->filename, self->sub, FALSE);
+        fen_sub_delete (self->sub);
+        self->sub = NULL;
+    }
+    
+    if (G_OBJECT_CLASS (g_fen_file_monitor_parent_class)->finalize)
+        (*G_OBJECT_CLASS (g_fen_file_monitor_parent_class)->finalize) (object);
+}
+
+static GObject *
+g_fen_file_monitor_constructor (GType type,
+  guint n_construct_properties,
+  GObjectConstructParam *construct_properties)
+{
+    GObject *obj;
+    GFenFileMonitorClass *klass;
+    GObjectClass *parent_class;
+    GFenFileMonitor *self;
+    const gchar *filename = NULL;
+  
+    klass = G_FEN_FILE_MONITOR_CLASS (g_type_class_peek (G_TYPE_FEN_FILE_MONITOR));
+    parent_class = g_fen_file_monitor_parent_class;
+    obj = parent_class->constructor (type,
+      n_construct_properties,
+      construct_properties);
+
+    self = G_FEN_FILE_MONITOR (obj);
+
+    filename = G_LOCAL_FILE_MONITOR (obj)->filename;
+
+    g_assert (filename != NULL);
+
+    /* Will never fail as is_supported() should be called before instanciating
+     * anyway */
+    if (!fen_init ())
+        g_assert_not_reached ();
+    
+    /* FIXME: what to do about errors here? we can't return NULL or another
+     * kind of error and an assertion is probably too hard */
+    self->sub = fen_sub_new (self, FALSE);
+    g_assert (self->sub);
+    
+    fen_add (filename, self->sub, FALSE);
+
+    return obj;
+}
+
+static gboolean
+g_fen_file_monitor_is_supported (void)
+{
+	return fen_init ();
+}
+
+static void
+g_fen_file_monitor_class_init (GFenFileMonitorClass* klass)
+{
+    GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+    GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass);
+    GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass);
+  
+    gobject_class->finalize = g_fen_file_monitor_finalize;
+    gobject_class->constructor = g_fen_file_monitor_constructor;
+    file_monitor_class->cancel = g_fen_file_monitor_cancel;
+
+    local_file_monitor_class->is_supported = g_fen_file_monitor_is_supported;
+}
+
+static void
+g_fen_file_monitor_init (GFenFileMonitor* monitor)
+{
+}
+
+static gboolean
+g_fen_file_monitor_cancel (GFileMonitor* monitor)
+{
+    GFenFileMonitor *self = G_FEN_FILE_MONITOR (monitor);
+    
+    if (self->sub) {
+        fen_remove (G_LOCAL_FILE_MONITOR (self)->filename, self->sub, FALSE);
+        fen_sub_delete (self->sub);
+        self->sub = NULL;
+    }
+    
+    if (G_FILE_MONITOR_CLASS (g_fen_file_monitor_parent_class)->cancel)
+        (*G_FILE_MONITOR_CLASS (g_fen_file_monitor_parent_class)->cancel) (monitor);
+
+    return TRUE;
+}
+

Added: trunk/gio/fen/gfenfilemonitor.h
==============================================================================
--- (empty file)
+++ trunk/gio/fen/gfenfilemonitor.h	Fri Mar 14 08:58:24 2008
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set expandtab ts=4 shiftwidth=4: */
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian DrÃge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl redhat com>
+ *          John McCutchan <john johnmccutchan com> 
+ *          Sebastian DrÃge <slomo circular-chaos org>
+ */
+
+#ifndef __G_FEN_FILE_MONITOR_H__
+#define __G_FEN_FILE_MONITOR_H__
+
+#include <glib-object.h>
+#include <string.h>
+#include <gio/gfilemonitor.h>
+#include <gio/glocalfilemonitor.h>
+#include <gio/giomodule.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FEN_FILE_MONITOR		(_g_fen_file_monitor_get_type ())
+#define G_FEN_FILE_MONITOR(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FEN_FILE_MONITOR, GFenFileMonitor))
+#define G_FEN_FILE_MONITOR_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_FEN_FILE_MONITOR, GFenFileMonitorClass))
+#define G_IS_FEN_FILE_MONITOR(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FEN_FILE_MONITOR))
+#define G_IS_FEN_FILE_MONITOR_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FEN_FILE_MONITOR))
+
+typedef struct _GFenFileMonitor      GFenFileMonitor;
+typedef struct _GFenFileMonitorClass GFenFileMonitorClass;
+
+struct _GFenFileMonitorClass {
+  GLocalFileMonitorClass parent_class;
+};
+
+GType _g_fen_file_monitor_get_type (void);
+
+G_END_DECLS
+
+#endif /* __G_FEN_FILE_MONITOR_H__ */

Modified: trunk/gio/giomodule.c
==============================================================================
--- trunk/gio/giomodule.c	(original)
+++ trunk/gio/giomodule.c	Fri Mar 14 08:58:24 2008
@@ -285,6 +285,8 @@
 
 G_LOCK_DEFINE_STATIC (loaded_dirs);
 
+extern GType _g_fen_directory_monitor_get_type (void);
+extern GType _g_fen_file_monitor_get_type (void);
 extern GType _g_inotify_directory_monitor_get_type (void);
 extern GType _g_inotify_file_monitor_get_type (void);
 extern GType _g_unix_volume_monitor_get_type (void);
@@ -331,9 +333,9 @@
 #if defined(HAVE_SYS_INOTIFY_H) || defined(HAVE_LINUX_INOTIFY_H)
       _g_inotify_directory_monitor_get_type ();
       _g_inotify_file_monitor_get_type ();
-#endif
-#ifdef G_OS_WIN32
-      g_win32_directory_monitor_get_type ();
+#elif defined(HAVE_FEN)
+      _g_fen_directory_monitor_get_type ();
+      _g_fen_file_monitor_get_type ();
 #endif
 #ifdef G_OS_UNIX
       _g_unix_volume_monitor_get_type ();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]