mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-01 23:13:40 +02:00
Added. Added. Added. Added. Added. Added. Added. Added. Added. Added.
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> svn path=/trunk/; revision=6702
This commit is contained in:
committed by
Alexander Larsson
parent
553b3b613b
commit
7dc75d707c
460
gio/fen/fen-node.c
Normal file
460
gio/fen/fen-node.c
Normal file
@@ -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;
|
||||
}
|
Reference in New Issue
Block a user