2 * Copyright (C) 1999, 2000 Red Hat Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include "xml-cache.h"
21 #include <gconf/gconf-internals.h>
26 /* This makes hash table safer when debugging */
27 #ifndef GCONF_ENABLE_DEBUG
28 #define safe_g_hash_table_insert g_hash_table_insert
31 safe_g_hash_table_insert(GHashTable* ht, gpointer key, gpointer value)
33 gpointer oldkey = NULL, oldval = NULL;
35 if (g_hash_table_lookup_extended(ht, key, &oldkey, &oldval))
37 gconf_log(GCL_WARNING, "Hash key `%s' is already in the table!",
43 g_hash_table_insert(ht, key, value);
48 static gboolean cache_is_nonexistent (Cache *cache,
50 static void cache_set_nonexistent (Cache *cache,
53 static void cache_unset_nonexistent (Cache *cache,
55 static void cache_insert (Cache *cache,
58 static void cache_remove_from_parent (Cache *cache,
60 static void cache_add_to_parent (Cache *cache,
63 static GHashTable *caches_by_root_dir = NULL;
68 GHashTable* nonexistent_cache;
75 cache_get (const gchar *root_dir,
81 if (caches_by_root_dir == NULL)
82 caches_by_root_dir = g_hash_table_new (g_str_hash, g_str_equal);
84 cache = g_hash_table_lookup (caches_by_root_dir, root_dir);
92 cache = g_new(Cache, 1);
94 cache->root_dir = g_strdup(root_dir);
96 cache->cache = g_hash_table_new(g_str_hash, g_str_equal);
97 cache->nonexistent_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
100 cache->dir_mode = dir_mode;
101 cache->file_mode = file_mode;
104 safe_g_hash_table_insert (caches_by_root_dir, cache->root_dir, cache);
109 static void cache_destroy_foreach(const gchar* key,
110 Dir* dir, gpointer data);
113 cache_unref (Cache *cache)
115 g_return_if_fail (cache != NULL);
116 g_return_if_fail (cache->refcount > 0);
118 if (cache->refcount > 1)
120 cache->refcount -= 1;
124 g_hash_table_remove (caches_by_root_dir, cache->root_dir);
125 if (g_hash_table_size (caches_by_root_dir) == 0)
127 g_hash_table_destroy (caches_by_root_dir);
128 caches_by_root_dir = NULL;
131 g_free(cache->root_dir);
132 g_hash_table_foreach(cache->cache, (GHFunc)cache_destroy_foreach,
134 g_hash_table_destroy(cache->cache);
135 g_hash_table_destroy(cache->nonexistent_cache);
141 typedef struct _SyncData SyncData;
145 gboolean deleted_some;
149 listify_foreach (gpointer key, gpointer value, gpointer data)
151 GSList **list = data;
153 *list = g_slist_prepend (*list, value);
157 cache_sync_foreach (Dir *dir,
160 GError* error = NULL;
165 /* log errors but don't report the specific ones */
166 if (!dir_sync (dir, &deleted, &error))
169 g_return_if_fail (error != NULL);
170 gconf_log (GCL_ERR, "%s", error->message);
171 g_error_free (error);
172 g_return_if_fail (dir_sync_pending (dir));
176 g_return_if_fail (error == NULL);
177 g_return_if_fail (!dir_sync_pending (dir));
181 /* Get rid of this directory */
182 cache_remove_from_parent (sd->dc, dir);
183 g_hash_table_remove (sd->dc->cache,
185 cache_set_nonexistent (sd->dc, dir_get_name (dir),
189 sd->deleted_some = TRUE;
195 dircmp (gconstpointer a,
198 Dir *dir_a = (Dir*) a;
199 Dir *dir_b = (Dir*) b;
200 const char *key_a = dir_get_name (dir_a);
201 const char *key_b = dir_get_name (dir_b);
203 /* This function is supposed to sort the list such that
204 * subdirectories are synced prior to their parents,
205 * thus ensuring that we are always able to get rid
206 * of directories that we don't need anymore.
208 * Keys with an ancestor/descendent relationship are always
209 * sorted with descendent before ancestor. Other keys are sorted
210 * in order to alphabetize directories, i.e. we find the common
211 * path segments and alphabetize the level below the common level.
212 * /foo/bar/a before /foo/bar/b, etc.
214 * This ensures that our sort function has proper semantics.
217 if (gconf_key_is_below (key_a, key_b))
218 return 1; /* a above b, so b is earlier in the list */
219 else if (gconf_key_is_below (key_b, key_a))
223 const char *ap = key_a;
224 const char *bp = key_b;
226 while (*ap && *bp && *ap == *bp)
232 if (*ap == '\0' && *bp == '\0')
235 /* we don't care about localization here,
236 * just some fixed order. Either
237 * *ap or *bp may be '\0' if you have keys like
238 * "foo" and "foo_bar"
248 cache_sync (Cache *cache,
251 SyncData sd = { FALSE, NULL, FALSE };
256 gconf_log (GCL_DEBUG, "Syncing the dir cache");
260 sd.deleted_some = FALSE;
262 /* get a list of everything; we can't filter by
263 * whether a sync is pending since we may make parents
264 * of removed directories dirty when we sync their child
268 g_hash_table_foreach (cache->cache, (GHFunc)listify_foreach, &list);
270 /* sort subdirs before parents */
271 list = g_slist_sort (list, dircmp);
274 g_slist_foreach (list, (GFunc) cache_sync_foreach, &sd);
276 /* If we deleted some subdirs, we may now be able to delete
277 * more parent dirs. So go ahead and do the sync again.
278 * Yeah this could be more efficient.
280 if (!sd.failed && sd.deleted_some)
283 if (sd.failed && err && *err == NULL)
285 gconf_set_error (err, GCONF_ERROR_FAILED,
286 _("Failed to sync XML cache contents to disk"));
292 typedef struct _CleanData CleanData;
300 cache_clean_foreach(const gchar* key,
301 Dir* dir, CleanData* cd)
305 last_access = dir_get_last_access(dir);
307 if ((cd->now - last_access) >= cd->length)
309 if (!dir_sync_pending(dir))
316 gconf_log(GCL_WARNING, _("Unable to remove directory `%s' from the XML backend cache, because it has not been successfully synced to disk"),
326 cache_clean (Cache *cache,
329 CleanData cd = { 0, NULL, 0 };
331 cd.length = older_than;
333 cd.now = time(NULL); /* ha ha, it's an online store! */
335 g_hash_table_foreach_remove(cache->cache, (GHRFunc)cache_clean_foreach,
339 size = g_hash_table_size(cache->cache);
342 gconf_log (GCL_DEBUG,
343 "%u items remain in the cache after cleaning already-synced items older than %u seconds",
350 cache_lookup (Cache *cache,
352 gboolean create_if_missing,
357 g_assert(key != NULL);
358 g_return_val_if_fail(cache != NULL, NULL);
361 dir = g_hash_table_lookup(cache->cache, key);
365 gconf_log(GCL_DEBUG, "Using dir %s from cache", key);
370 /* Not in cache, check whether we already failed
372 if (cache_is_nonexistent(cache, key))
374 if (!create_if_missing)
379 /* Didn't already fail to load, try to load */
380 dir = dir_load (key, cache->root_dir, err);
384 g_assert(err == NULL || *err == NULL);
386 /* Cache it and add to parent */
387 cache_insert (cache, dir);
388 cache_add_to_parent (cache, dir);
394 /* Remember that we failed to load it */
395 if (!create_if_missing)
397 cache_set_nonexistent(cache, key, TRUE);
413 g_assert(dir == NULL);
414 g_assert(create_if_missing);
415 g_assert(err == NULL || *err == NULL);
419 gconf_log(GCL_DEBUG, "Creating new dir %s", key);
421 dir = dir_new(key, cache->root_dir, cache->dir_mode, cache->file_mode);
423 if (!dir_ensure_exists(dir, err))
427 g_return_val_if_fail((err == NULL) ||
434 cache_insert (cache, dir);
435 cache_add_to_parent (cache, dir);
436 cache_unset_nonexistent (cache, dir_get_name (dir));
444 cache_is_nonexistent(Cache* cache,
447 return GPOINTER_TO_INT(g_hash_table_lookup(cache->nonexistent_cache,
452 cache_set_nonexistent (Cache* cache,
458 /* don't use safe_ here, doesn't matter */
459 g_hash_table_insert(cache->nonexistent_cache,
461 GINT_TO_POINTER(TRUE));
464 g_hash_table_remove(cache->nonexistent_cache, key);
468 cache_unset_nonexistent (Cache *cache,
473 g_return_if_fail (key != NULL);
475 cache_set_nonexistent (cache, key, FALSE);
477 if (strcmp (key, "/") == 0)
480 parent_key = gconf_key_directory (key);
482 cache_unset_nonexistent (cache, parent_key);
488 cache_insert (Cache* cache,
491 g_return_if_fail(d != NULL);
493 gconf_log(GCL_DEBUG, "Caching dir %s", dir_get_name(d));
495 safe_g_hash_table_insert(cache->cache, (gchar*)dir_get_name(d), d);
499 cache_destroy_foreach(const gchar* key,
500 Dir* dir, gpointer data)
502 #ifdef GCONF_ENABLE_DEBUG
503 if (dir_sync_pending (dir))
504 gconf_log(GCL_DEBUG, "Destroying a directory (%s) with sync still pending",
511 cache_remove_from_parent (Cache *cache,
517 /* We have to actually force a load here, to decide
518 * whether to delete the parent.
520 parent = cache_lookup (cache, dir_get_parent_name (d),
523 /* parent == d means d is the root dir */
524 if (parent == NULL || parent == d)
527 name = gconf_key_key (dir_get_name (d));
529 dir_child_removed (parent, name);
533 cache_add_to_parent (Cache *cache,
539 parent = cache_lookup (cache, dir_get_parent_name (d),
542 /* parent == d means d is the root dir */
543 if (parent == NULL || parent == d)
546 name = gconf_key_key (dir_get_name (d));
548 dir_child_added (parent, name);
553 xml_test_cache (void)
555 #ifndef GCONF_DISABLE_TESTS