4 * FSCache - a glib-style object for caching files
5 * Copyright (C) 2005, the ROX-Filer team.
7 * A cache object holds stat details about files in a hash table, along
8 * with user-specified data. When you want to read in a file try to
9 * get the data via the cache - if the file is cached AND has not been
10 * modified since it was last loaded the cached copy is returned, else the
13 * The actual data need not be the raw file contents - a user specified
14 * function loads the file and associates data with the file in the cache.
16 * This program is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the Free
18 * Software Foundation; either version 2 of the License, or (at your option)
21 * This program is distributed in the hope that it will be useful, but WITHOUT
22 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
26 * You should have received a copy of the GNU General Public License along with
27 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
28 * Place, Suite 330, Boston, MA 02111-1307 USA
37 typedef struct _GFSCacheKey GFSCacheKey
;
38 typedef struct _GFSCacheData GFSCacheData
;
42 GHashTable
*inode_to_stats
;
56 GObject
*data
; /* The object from the file */
59 /* Details of the file last time we checked it */
60 time_t m_time
, c_time
;
65 #define UPTODATE(data, info) \
66 (data->m_time == info.st_mtime \
67 && data->c_time == info.st_ctime \
68 && data->length == info.st_size \
69 && data->mode == info.st_mode) \
72 /* Static prototypes */
74 static guint
hash_key(gconstpointer key
);
75 static gint
cmp_stats(gconstpointer a
, gconstpointer b
);
76 static void destroy_hash_entry(gpointer key
, gpointer data
, gpointer user_data
);
77 static gboolean
purge_hash_entry(gpointer key
, gpointer data
,
79 static GFSCacheData
*lookup_internal(GFSCache
*cache
, const char *pathname
,
80 FSCacheLookup lookup_type
);
90 /****************************************************************
91 * EXTERNAL INTERFACE *
92 ****************************************************************/
95 /* Create a new GFSCache object and return a pointer to it.
97 * When someone tries to lookup a file which is not in the cache,
99 * It should load the file and return a pointer to an object for the file.
100 * The object should have a ref count of 1.
102 * ref() and unref() should modify the reference counter of the object.
103 * When the counter reaches zero, destroy the object.
105 * getref() returns the current value. This can be used to stop objects
106 * being purged from the cache while they are being used elsewhere, which
107 * is rather wasteful. If NULL then this check isn't done.
109 * update() will be called to update an object which is cached, but
110 * out of date. If NULL, the object will be unref'd and load() used
113 * 'user_data' will be passed to all of the above functions.
115 GFSCache
*g_fscache_new(GFSLoadFunc load
,
116 GFSUpdateFunc update
,
121 cache
= g_new(GFSCache
, 1);
122 cache
->inode_to_stats
= g_hash_table_new(hash_key
, cmp_stats
);
124 cache
->update
= update
;
125 cache
->user_data
= user_data
;
130 void g_fscache_destroy(GFSCache
*cache
)
132 g_return_if_fail(cache
!= NULL
);
134 g_hash_table_foreach(cache
->inode_to_stats
, destroy_hash_entry
, NULL
);
135 g_hash_table_destroy(cache
->inode_to_stats
);
140 /* Find the data for this file in the cache, loading it into
141 * the cache if it isn't there already.
143 * Remember to g_object_unref() the returned value when
144 * you're done with it.
146 * Returns NULL on failure.
148 gpointer
g_fscache_lookup(GFSCache
*cache
, const char *pathname
)
150 return g_fscache_lookup_full(cache
, pathname
,
151 FSCACHE_LOOKUP_CREATE
, NULL
);
154 /* Force this already-loaded item into the cache. The cache will
155 * ref the object if it wants to keep it.
156 * If update_details is FALSE then the timestamp and size aren't recorded.
157 * Generally, you call this function with update_details = TRUE when you
158 * start loading some data and then with update_details = FALSE when you
159 * put in the loaded object.
161 void g_fscache_insert(GFSCache
*cache
, const char *pathname
, gpointer obj
,
162 gboolean update_details
)
166 data
= lookup_internal(cache
, pathname
,
167 update_details
? FSCACHE_LOOKUP_INIT
168 : FSCACHE_LOOKUP_INSERT
);
176 g_object_unref(data
->data
);
180 /* As g_fscache_lookup, but 'lookup_type' controls what happens if the data
182 * If found is not NULL, use it to indicate whether something is being
183 * returned (a NULL return could indicate that the data is cached, but
185 * If returned value is not NULL, value is refd.
187 gpointer
g_fscache_lookup_full(GFSCache
*cache
, const char *pathname
,
188 FSCacheLookup lookup_type
,
193 g_return_val_if_fail(lookup_type
!= FSCACHE_LOOKUP_INIT
, NULL
);
195 data
= lookup_internal(cache
, pathname
, lookup_type
);
208 g_object_ref(data
->data
);
213 /* Call the update() function on this item if it's in the cache
214 * AND it's out-of-date.
216 void g_fscache_may_update(GFSCache
*cache
, const char *pathname
)
222 g_return_if_fail(cache
!= NULL
);
223 g_return_if_fail(pathname
!= NULL
);
224 g_return_if_fail(cache
->update
!= NULL
);
226 if (mc_stat(pathname
, &info
))
229 key
.device
= info
.st_dev
;
230 key
.inode
= info
.st_ino
;
232 data
= g_hash_table_lookup(cache
->inode_to_stats
, &key
);
234 if (data
&& !UPTODATE(data
, info
))
236 cache
->update(data
->data
, pathname
, cache
->user_data
);
237 data
->m_time
= info
.st_mtime
;
238 data
->c_time
= info
.st_ctime
;
239 data
->length
= info
.st_size
;
240 data
->mode
= info
.st_mode
;
244 /* Call the update() function on this item iff it's in the cache. */
245 void g_fscache_update(GFSCache
*cache
, const char *pathname
)
251 g_return_if_fail(cache
!= NULL
);
252 g_return_if_fail(pathname
!= NULL
);
253 g_return_if_fail(cache
->update
!= NULL
);
255 if (mc_stat(pathname
, &info
))
258 key
.device
= info
.st_dev
;
259 key
.inode
= info
.st_ino
;
261 data
= g_hash_table_lookup(cache
->inode_to_stats
, &key
);
265 cache
->update(data
->data
, pathname
, cache
->user_data
);
266 data
->m_time
= info
.st_mtime
;
267 data
->c_time
= info
.st_ctime
;
268 data
->length
= info
.st_size
;
269 data
->mode
= info
.st_mode
;
273 /* Remove all cache entries last accessed more than 'age' seconds
276 void g_fscache_purge(GFSCache
*cache
, gint age
)
278 struct PurgeInfo info
;
280 g_return_if_fail(cache
!= NULL
);
284 info
.now
= time(NULL
);
286 g_hash_table_foreach_remove(cache
->inode_to_stats
, purge_hash_entry
,
291 /****************************************************************
292 * INTERNAL FUNCTIONS *
293 ****************************************************************/
296 /* Generate a hash number for some stats */
297 static guint
hash_key(gconstpointer key
)
299 GFSCacheKey
*stats
= (GFSCacheKey
*) key
;
304 /* See if two stats blocks represent the same file */
305 static gint
cmp_stats(gconstpointer a
, gconstpointer b
)
307 GFSCacheKey
*c
= (GFSCacheKey
*) a
;
308 GFSCacheKey
*d
= (GFSCacheKey
*) b
;
310 return c
->device
== d
->device
&& c
->inode
== d
->inode
;
313 static void destroy_hash_entry(gpointer key
, gpointer data
, gpointer user_data
)
315 GFSCacheData
*cache_data
= (GFSCacheData
*) data
;
317 if (cache_data
->data
)
318 g_object_unref(cache_data
->data
);
324 static gboolean
purge_hash_entry(gpointer key
, gpointer data
,
327 struct PurgeInfo
*info
= (struct PurgeInfo
*) user_data
;
328 GFSCacheData
*cache_data
= (GFSCacheData
*) data
;
330 /* It's wasteful to remove an entry if someone else is using it */
331 if (cache_data
->data
&& cache_data
->data
->ref_count
> 1)
334 if (cache_data
->last_lookup
<= info
->now
335 && cache_data
->last_lookup
>= info
->now
- info
->age
)
338 if (cache_data
->data
)
339 g_object_unref(cache_data
->data
);
347 /* As for g_fscache_lookup_full, but return the GFSCacheData rather than
348 * the data it contains. Doesn't increment the refcount.
350 static GFSCacheData
*lookup_internal(GFSCache
*cache
, const char *pathname
,
351 FSCacheLookup lookup_type
)
357 g_return_val_if_fail(cache
!= NULL
, NULL
);
358 g_return_val_if_fail(pathname
!= NULL
, NULL
);
360 if (mc_stat(pathname
, &info
))
363 key
.device
= info
.st_dev
;
364 key
.inode
= info
.st_ino
;
366 data
= g_hash_table_lookup(cache
->inode_to_stats
, &key
);
370 /* We've cached this file already */
372 if (lookup_type
== FSCACHE_LOOKUP_PEEK
||
373 lookup_type
== FSCACHE_LOOKUP_INSERT
)
374 goto out
; /* Never update on peeks */
376 if (lookup_type
== FSCACHE_LOOKUP_INIT
)
379 /* Is it up-to-date? */
381 if (UPTODATE(data
, info
))
384 if (lookup_type
== FSCACHE_LOOKUP_ONLY_NEW
)
389 cache
->update(data
->data
, pathname
, cache
->user_data
);
393 g_object_unref(data
->data
);
399 GFSCacheKey
*new_key
;
401 if (lookup_type
!= FSCACHE_LOOKUP_CREATE
&&
402 lookup_type
!= FSCACHE_LOOKUP_INIT
)
405 new_key
= g_memdup(&key
, sizeof(key
));
407 data
= g_new(GFSCacheData
, 1);
410 g_hash_table_insert(cache
->inode_to_stats
, new_key
, data
);
414 data
->m_time
= info
.st_mtime
;
415 data
->c_time
= info
.st_ctime
;
416 data
->length
= info
.st_size
;
417 data
->mode
= info
.st_mode
;
419 if (data
->data
== NULL
&&
420 lookup_type
!= FSCACHE_LOOKUP_INIT
&&
421 lookup_type
!= FSCACHE_LOOKUP_INSERT
)
423 /* Create the object for the file (ie, not an update) */
425 data
->data
= cache
->load(pathname
, cache
->user_data
);
428 data
->last_lookup
= time(NULL
);