~ubuntu-branches/ubuntu/oneiric/gconf/oneiric-proposed

« back to all changes in this revision

Viewing changes to backends/xml-cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Takuo KITAME
  • Date: 2002-03-17 01:51:39 UTC
  • Revision ID: james.westby@ubuntu.com-20020317015139-z4f8fdg1hoe049g0
Tags: upstream-1.0.9
ImportĀ upstreamĀ versionĀ 1.0.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GConf
 
2
 * Copyright (C) 1999, 2000 Red Hat Inc.
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 */
 
19
 
 
20
#include "xml-cache.h"
 
21
#include <gconf/gconf-internals.h>
 
22
 
 
23
#include <time.h>
 
24
 
 
25
/* This makes hash table safer when debugging */
 
26
#ifndef GCONF_ENABLE_DEBUG
 
27
#define safe_g_hash_table_insert g_hash_table_insert
 
28
#else
 
29
static void
 
30
safe_g_hash_table_insert(GHashTable* ht, gpointer key, gpointer value)
 
31
{
 
32
  gpointer oldkey = NULL, oldval = NULL;
 
33
 
 
34
  if (g_hash_table_lookup_extended(ht, key, &oldkey, &oldval))
 
35
    {
 
36
      gconf_log(GCL_WARNING, "Hash key `%s' is already in the table!",
 
37
                (gchar*)key);
 
38
      return;
 
39
    }
 
40
  else
 
41
    {
 
42
      g_hash_table_insert(ht, key, value);
 
43
    }
 
44
}
 
45
#endif
 
46
 
 
47
static gboolean
 
48
cache_is_nonexistent(Cache* cache,
 
49
                     const gchar* key);
 
50
 
 
51
static void
 
52
cache_set_nonexistent   (Cache* cache,
 
53
                         const gchar* key,
 
54
                         gboolean setting);
 
55
 
 
56
static void
 
57
cache_insert (Cache* cache,
 
58
              Dir* d);
 
59
 
 
60
struct _Cache {
 
61
  gchar* root_dir;
 
62
  GHashTable* cache;
 
63
  GHashTable* nonexistent_cache;
 
64
  /*
 
65
    List of lists of dirs marked deleted, in the
 
66
    proper order; should be synced by deleting each
 
67
    list from front to end, starting with the first
 
68
    list.
 
69
  */ 
 
70
  GSList* deleted;
 
71
  guint dir_mode;
 
72
  guint file_mode;
 
73
};
 
74
 
 
75
Cache*
 
76
cache_new (const gchar  *root_dir,
 
77
           guint dir_mode,
 
78
           guint file_mode)
 
79
{
 
80
  Cache* cache;
 
81
 
 
82
  cache = g_new(Cache, 1);
 
83
 
 
84
  cache->root_dir = g_strdup(root_dir);
 
85
 
 
86
  cache->cache = g_hash_table_new(g_str_hash, g_str_equal);
 
87
  cache->nonexistent_cache = g_hash_table_new(g_str_hash, g_str_equal);
 
88
 
 
89
  cache->deleted = NULL;
 
90
 
 
91
  cache->dir_mode = dir_mode;
 
92
  cache->file_mode = file_mode;
 
93
  
 
94
  return cache;
 
95
}
 
96
 
 
97
static void cache_destroy_foreach(const gchar* key,
 
98
                                  Dir* dir, gpointer data);
 
99
 
 
100
static void cache_destroy_nonexistent_foreach(gchar* key,
 
101
                                              gpointer val,
 
102
                                              gpointer data);
 
103
 
 
104
void
 
105
cache_destroy (Cache        *cache)
 
106
{
 
107
  GSList *iter;
 
108
  
 
109
  g_free(cache->root_dir);
 
110
  g_hash_table_foreach(cache->cache, (GHFunc)cache_destroy_foreach,
 
111
                       NULL);
 
112
  g_hash_table_foreach(cache->nonexistent_cache,
 
113
                       (GHFunc)cache_destroy_nonexistent_foreach,
 
114
                       NULL);
 
115
  g_hash_table_destroy(cache->cache);
 
116
  g_hash_table_destroy(cache->nonexistent_cache);
 
117
 
 
118
  if (cache->deleted != NULL)
 
119
    gconf_log(GCL_WARNING, _("Unsynced directory deletions when shutting down XML backend"));
 
120
  
 
121
  iter = cache->deleted;
 
122
 
 
123
  while (iter != NULL)
 
124
    {
 
125
      g_slist_free(iter->data);
 
126
 
 
127
      iter = g_slist_next(iter);
 
128
    }
 
129
  g_slist_free(cache->deleted);
 
130
  
 
131
  g_free(cache);
 
132
}
 
133
 
 
134
 
 
135
typedef struct _SyncData SyncData;
 
136
struct _SyncData {
 
137
  gboolean failed;
 
138
  Cache* dc;
 
139
};
 
140
 
 
141
static void
 
142
cache_sync_foreach(const gchar* key,
 
143
                   Dir* dir,
 
144
                   SyncData* sd)
 
145
{
 
146
  GError* error = NULL;
 
147
  
 
148
  /* log errors but don't report the specific ones */
 
149
  if (!dir_sync(dir, &error))
 
150
    {
 
151
      sd->failed = TRUE;
 
152
      g_return_if_fail(error != NULL);
 
153
      gconf_log(GCL_ERR, "%s", error->message);
 
154
      g_error_free(error);
 
155
      g_return_if_fail(dir_sync_pending(dir));
 
156
    }
 
157
  else
 
158
    {
 
159
      g_return_if_fail(error == NULL);
 
160
      g_return_if_fail(!dir_sync_pending(dir));
 
161
    }
 
162
}
 
163
 
 
164
gboolean
 
165
cache_sync       (Cache        *cache,
 
166
                  GError  **err)
 
167
{
 
168
  SyncData sd = { FALSE, NULL };
 
169
  GSList* delete_list;
 
170
  sd.dc = cache;
 
171
 
 
172
  /* First delete pending directories */
 
173
  delete_list = cache->deleted;
 
174
 
 
175
  while (delete_list != NULL)
 
176
    {
 
177
      GSList* tmp;
 
178
 
 
179
      tmp = delete_list->data;
 
180
 
 
181
      while (tmp != NULL)
 
182
        {
 
183
          Dir* d = tmp->data;
 
184
 
 
185
          if (!dir_sync(d, NULL)) /* don't get errors, they'd pile up */
 
186
            sd.failed = TRUE;
 
187
          
 
188
          tmp = g_slist_next(tmp);
 
189
        }
 
190
 
 
191
      g_slist_free(delete_list->data);
 
192
      
 
193
      delete_list = g_slist_next(delete_list);
 
194
    }
 
195
 
 
196
  g_slist_free(cache->deleted);
 
197
  cache->deleted = NULL;
 
198
  
 
199
  g_hash_table_foreach(cache->cache, (GHFunc)cache_sync_foreach,
 
200
                       &sd);
 
201
 
 
202
  return !sd.failed;  
 
203
}
 
204
 
 
205
typedef struct _CleanData CleanData;
 
206
struct _CleanData {
 
207
  GTime now;
 
208
  Cache* cache;
 
209
  GTime length;
 
210
};
 
211
 
 
212
static gboolean
 
213
cache_clean_foreach(const gchar* key,
 
214
                    Dir* dir, CleanData* cd)
 
215
{
 
216
  GTime last_access;
 
217
 
 
218
  last_access = dir_get_last_access(dir);
 
219
 
 
220
  if ((cd->now - last_access) >= cd->length)
 
221
    {
 
222
      if (!dir_sync_pending(dir))
 
223
        {
 
224
          dir_destroy(dir);
 
225
          return TRUE;
 
226
        }
 
227
      else
 
228
        {
 
229
          gconf_log(GCL_WARNING, _("Unable to remove directory `%s' from the XML backend cache, because it has not been successfully synced to disk"),
 
230
                    dir_get_name(dir));
 
231
          return FALSE;
 
232
        }
 
233
    }
 
234
  else
 
235
    return FALSE;
 
236
}
 
237
 
 
238
void
 
239
cache_clean      (Cache        *cache,
 
240
                  GTime         older_than)
 
241
{
 
242
  CleanData cd = { 0, 0, 0 };
 
243
  guint size;
 
244
  cd.cache = cache;
 
245
  cd.length = older_than;
 
246
  
 
247
  cd.now = time(NULL); /* ha ha, it's an online store! */
 
248
  
 
249
  g_hash_table_foreach_remove(cache->cache, (GHRFunc)cache_clean_foreach,
 
250
                              &cd);
 
251
 
 
252
  size = g_hash_table_size(cache->cache);
 
253
 
 
254
  if (size != 0)
 
255
    gconf_log (GCL_DEBUG,
 
256
               _("%u items remain in the cache after cleaning already-synced items older than %u seconds"),
 
257
               size,
 
258
               older_than);
 
259
}
 
260
 
 
261
static void
 
262
cache_delete_dir_by_pointer(Cache* cache,
 
263
                            Dir * d,
 
264
                            GError** err);
 
265
 
 
266
static void
 
267
cache_delete_recursive(Cache* cache, Dir* d, GSList** hit_list, GError** err)
 
268
{  
 
269
  GSList* subdirs;
 
270
  GSList* tmp;
 
271
  gboolean failure = FALSE;
 
272
  
 
273
  subdirs = dir_all_subdirs(d, err);
 
274
 
 
275
  if (subdirs == NULL && err && *err != NULL)
 
276
    failure = TRUE;
 
277
  
 
278
  tmp = subdirs;
 
279
  while (tmp != NULL && !failure)
 
280
    {
 
281
      Dir* subd;
 
282
      gchar* fullkey;
 
283
 
 
284
      fullkey = gconf_concat_dir_and_key(dir_get_name(d), (gchar*)tmp->data);
 
285
      
 
286
      subd = cache_lookup(cache, fullkey, FALSE, err);
 
287
 
 
288
      g_free(tmp->data);
 
289
      g_free(fullkey);
 
290
      
 
291
      if (subd == NULL && err && *err)
 
292
        failure = TRUE;
 
293
      else if (subd != NULL &&
 
294
               !dir_is_deleted(subd))
 
295
        {
 
296
          /* recurse, whee! (unless the subdir is already deleted) */
 
297
          cache_delete_dir_by_pointer(cache, subd, err);
 
298
 
 
299
          if (err && *err)
 
300
            failure = TRUE;
 
301
        }
 
302
          
 
303
      tmp = g_slist_next(tmp);
 
304
    }
 
305
 
 
306
  g_slist_free(subdirs);
 
307
  
 
308
  /* The first directories to be deleted (fringes) go on the front
 
309
     of the list. */
 
310
  *hit_list = g_slist_prepend(*hit_list, d);
 
311
  
 
312
  /* We go ahead and mark the dir deleted */
 
313
  dir_mark_deleted(d);
 
314
 
 
315
  /* be sure we set error if failure occurred */
 
316
  g_return_if_fail( (!failure) || (err == NULL) || (*err != NULL));
 
317
}
 
318
 
 
319
 
 
320
static void
 
321
cache_delete_dir_by_pointer(Cache* cache,
 
322
                            Dir * d,
 
323
                            GError** err)
 
324
{
 
325
  GSList* hit_list = NULL;
 
326
 
 
327
  cache_delete_recursive(cache, d, &hit_list, err);
 
328
 
 
329
  /* If you first dir_cache_delete() a subdir, then dir_cache_delete()
 
330
     its parent, without syncing, first the list generated by
 
331
     the subdir delete then the list from the parent delete should
 
332
     be nuked. If you first delete a parent, then its subdir,
 
333
     really only the parent list should be nuked, but
 
334
     in effect it's OK to nuke the parent first then
 
335
     fail to nuke the subdir. So, if we prepend here,
 
336
     then nuke the list in order, it will work fine.
 
337
  */
 
338
  
 
339
  cache->deleted = g_slist_prepend(cache->deleted, hit_list);
 
340
}
 
341
 
 
342
void
 
343
cache_delete_dir (Cache        *cache,
 
344
                  const gchar  *key,
 
345
                  GError  **err)
 
346
{
 
347
  Dir* d;
 
348
 
 
349
  d = cache_lookup(cache, key, FALSE, err);
 
350
 
 
351
  if (d != NULL)
 
352
    {
 
353
      g_assert(err == NULL || *err == NULL);
 
354
      cache_delete_dir_by_pointer(cache, d, err);
 
355
    }
 
356
}
 
357
 
 
358
Dir*
 
359
cache_lookup     (Cache        *cache,
 
360
                  const gchar  *key,
 
361
                  gboolean create_if_missing,
 
362
                  GError  **err)
 
363
{
 
364
  Dir* dir;
 
365
  
 
366
  g_assert(key != NULL);
 
367
  g_return_val_if_fail(cache != NULL, NULL);
 
368
  
 
369
  /* Check cache */
 
370
  dir = g_hash_table_lookup(cache->cache, key);
 
371
  
 
372
  if (dir != NULL)
 
373
    {
 
374
      gconf_log(GCL_DEBUG, "Using dir %s from cache", key);
 
375
      return dir;
 
376
    }
 
377
  else
 
378
    {
 
379
      /* Not in cache, check whether we already failed
 
380
         to load it */
 
381
      if (cache_is_nonexistent(cache, key))
 
382
        {
 
383
          if (!create_if_missing)
 
384
            return NULL;
 
385
        }
 
386
      else
 
387
        {
 
388
          /* Didn't already fail to load, try to load */
 
389
          dir = dir_load(key, cache->root_dir, err);
 
390
          
 
391
          if (dir != NULL)
 
392
            {
 
393
              g_assert(err == NULL || *err == NULL);
 
394
              
 
395
              /* Cache it */
 
396
              cache_insert(cache, dir);
 
397
              
 
398
              return dir;
 
399
            }
 
400
          else
 
401
            {
 
402
              /* Remember that we failed to load it */
 
403
              if (!create_if_missing)
 
404
                {
 
405
                  cache_set_nonexistent(cache, key, TRUE);
 
406
              
 
407
                  return NULL;
 
408
                }
 
409
              else
 
410
                {
 
411
                  if (err && *err)
 
412
                    {
 
413
                      g_error_free(*err);
 
414
                      *err = NULL;
 
415
                    }
 
416
                }
 
417
            }
 
418
        }
 
419
    }
 
420
  
 
421
  g_assert(dir == NULL);
 
422
  g_assert(create_if_missing);
 
423
  g_assert(err == NULL || *err == NULL);
 
424
  
 
425
  if (dir == NULL)
 
426
    {
 
427
      gconf_log(GCL_DEBUG, "Creating new dir %s", key);
 
428
      
 
429
      dir = dir_new(key, cache->root_dir, cache->dir_mode, cache->file_mode);
 
430
 
 
431
      if (!dir_ensure_exists(dir, err))
 
432
        {
 
433
          dir_destroy(dir);
 
434
          
 
435
          g_return_val_if_fail((err == NULL) ||
 
436
                               (*err != NULL) ,
 
437
                               NULL);
 
438
          return NULL;
 
439
        }
 
440
      else
 
441
        cache_insert(cache, dir);
 
442
    }
 
443
 
 
444
  return dir;
 
445
}
 
446
 
 
447
static gboolean
 
448
cache_is_nonexistent(Cache* cache,
 
449
                     const gchar* key)
 
450
{
 
451
  return GPOINTER_TO_INT(g_hash_table_lookup(cache->nonexistent_cache,
 
452
                                             key));
 
453
}
 
454
 
 
455
static void
 
456
cache_set_nonexistent   (Cache* cache,
 
457
                         const gchar* key,
 
458
                         gboolean setting)
 
459
{
 
460
  if (setting)
 
461
    {
 
462
      /* don't use safe_ here, doesn't matter */
 
463
      g_hash_table_insert(cache->nonexistent_cache,
 
464
                          g_strdup(key),
 
465
                          GINT_TO_POINTER(TRUE));
 
466
    }
 
467
  else
 
468
    {
 
469
      gpointer origkey;
 
470
      gpointer origval;
 
471
 
 
472
      if (g_hash_table_lookup_extended(cache->nonexistent_cache,
 
473
                                       key,
 
474
                                       &origkey, &origval))
 
475
        {
 
476
          g_free(origkey);
 
477
          g_hash_table_remove(cache->nonexistent_cache,
 
478
                              key);
 
479
        }
 
480
    }
 
481
}
 
482
 
 
483
static void
 
484
cache_insert (Cache* cache,
 
485
              Dir* d)
 
486
{
 
487
  g_return_if_fail(d != NULL);
 
488
 
 
489
  gconf_log(GCL_DEBUG, "Caching dir %s", dir_get_name(d));
 
490
  
 
491
  safe_g_hash_table_insert(cache->cache, (gchar*)dir_get_name(d), d);
 
492
}
 
493
 
 
494
static void
 
495
cache_destroy_foreach(const gchar* key,
 
496
                      Dir* dir, gpointer data)
 
497
{
 
498
#ifdef GCONF_ENABLE_DEBUG
 
499
  if (dir_sync_pending(dir))
 
500
    gconf_log(GCL_DEBUG, "Destroying a directory (%s) with sync still pending",
 
501
              dir_get_name(dir));
 
502
#endif
 
503
  dir_destroy(dir);
 
504
}
 
505
 
 
506
static void
 
507
cache_destroy_nonexistent_foreach(gchar* key,
 
508
                                  gpointer val,
 
509
                                  gpointer data)
 
510
{
 
511
  g_free(key);
 
512
}
 
513
 
 
514
 
 
515
 
 
516