Back to index

avfs  1.0.1
cache.c
Go to the documentation of this file.
00001 /*
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 1998-2001  Miklos Szeredi <miklos@szeredi.hu>
00004     Copyright (C) 2006       Ralf Hoffmann (ralf@boomerangsworld.de)
00005 
00006     This program can be distributed under the terms of the GNU GPL.
00007     See the file COPYING.
00008 */
00009 
00010 /* 
00011    TODO:
00012    
00013    Virtual filesystem where all the cached files can be found.
00014 
00015    There are two interfaces available:
00016    The av_cacheobj* functions are built around the cacheobj structure.
00017      The user holds the reference to it and can use it to access the
00018      stored object. The cache itself creates the cacheobj but doesn't
00019      hold a reference to it so it doesn't destroy the cacheobj but
00020      will unref the object stored in the cacheobj struct if the space
00021      is needed.
00022    The av_cache2* functions are built around the name as a key for
00023      stored object. The cacheobj is not return and the cache holds the
00024      only reference to it and will destroy it at some point.
00025 
00026 */
00027 
00028 #include "cache.h"
00029 #include "internal.h"
00030 #include "exit.h"
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 
00035 struct cacheobj {
00036     void *obj;
00037     avoff_t diskusage;
00038     char *name;
00039 
00040     struct cacheobj *next;
00041     struct cacheobj *prev;
00042 
00043     int internal_obj;
00044 };
00045 
00046 #define MBYTE (1024 * 1024)
00047 
00048 static AV_LOCK_DECL(cachelock);
00049 static struct cacheobj cachelist;
00050 static avoff_t disk_cache_limit = 100 * MBYTE;
00051 static avoff_t disk_keep_free = 10 * MBYTE;
00052 static avoff_t disk_usage = 0;
00053 
00054 static int cache_clear();
00055 
00056 static int cache_getfunc(struct entry *ent, const char *param, char **retp)
00057 {
00058     *retp = av_strdup("");
00059     return 0;
00060 }
00061 
00062 static int cache_setfunc(struct entry *ent, const char *param, const char *val)
00063 {
00064     struct statefile *sf = (struct statefile *) av_namespace_get(ent);
00065     int (*func)() = (int (*)()) sf->data;
00066     
00067     if(strlen(val) > 0)
00068         return func();
00069 
00070     return 0;
00071 }
00072 
00073 static int cache_getoff(struct entry *ent, const char *param, char **retp)
00074 {
00075     char buf[64];
00076     struct statefile *sf = (struct statefile *) av_namespace_get(ent);
00077     avoff_t *offp = (avoff_t *)  sf->data;
00078 
00079     AV_LOCK(cachelock);
00080     sprintf(buf, "%llu\n", *offp);
00081     AV_UNLOCK(cachelock);
00082 
00083     *retp = av_strdup(buf);
00084     return 0;
00085 }
00086 
00087 static int cache_setoff(struct entry *ent, const char *param, const char *val)
00088 {
00089     struct statefile *sf = (struct statefile *) av_namespace_get(ent);
00090     avoff_t *offp = (avoff_t *) sf->data;
00091     avoff_t offval;
00092     char *end;
00093     
00094     /* Make truncate work with fuse */
00095     if(!val[0])
00096         offval = 0;
00097     else {
00098         offval = strtoll(val, &end, 0);
00099         if(end == val)
00100             return -EINVAL;
00101         if(*end == '\n')
00102             end ++;
00103         if(*end != '\0')
00104             return -EINVAL;
00105         if(offval < 0)
00106             return -EINVAL;
00107     }
00108 
00109     AV_LOCK(cachelock);
00110     *offp = offval;
00111     AV_UNLOCK(cachelock);
00112 
00113     return 0;
00114 }
00115 
00120 static void destroy_cache()
00121 {
00122     struct cacheobj *cobj;
00123 
00124     AV_LOCK(cachelock);
00125     for(cobj = &cachelist; cobj->next != &cachelist; ) {
00126         if(cobj->next->internal_obj) {
00127             /* unref the internal objects which will remove it */
00128             av_unref_obj(cobj->next);
00129         } else {
00130             /* this shouldn't happen, there shouldn't be
00131              * any external object left at exit */
00132             cobj = cobj->next;
00133         }
00134     }
00135     AV_UNLOCK(cachelock);
00136 }
00137 
00138 void av_init_cache()
00139 {
00140     struct statefile statf;
00141 
00142     cachelist.next = &cachelist;
00143     cachelist.prev = &cachelist;
00144 
00145     statf.get = cache_getoff;
00146     statf.set = cache_setoff;
00147  
00148     statf.data = &disk_cache_limit;
00149     av_avfsstat_register("cache/limit", &statf);
00150     
00151     statf.data = &disk_keep_free;
00152     av_avfsstat_register("cache/keep_free", &statf);
00153 
00154     statf.set = NULL;
00155     statf.data = &disk_usage;
00156     av_avfsstat_register("cache/usage", &statf);
00157 
00158     statf.set = cache_setfunc;
00159     statf.get = cache_getfunc;
00160     statf.data = cache_clear;
00161     av_avfsstat_register("cache/clear", &statf);
00162     
00163     av_add_exithandler(destroy_cache);
00164 }
00165 
00166 static void cacheobj_remove(struct cacheobj *cobj)
00167 {
00168     struct cacheobj *next;
00169     struct cacheobj *prev;
00170     
00171     next = cobj->next;
00172     prev = cobj->prev;
00173     next->prev = prev;
00174     prev->next = next;
00175 }
00176 
00177 static void cacheobj_insert(struct cacheobj *cobj)
00178 {
00179     struct cacheobj *next;
00180     struct cacheobj *prev;
00181 
00182     next = cachelist.next;
00183     prev = &cachelist;
00184     next->prev = cobj;
00185     prev->next = cobj;
00186     cobj->next = next;
00187     cobj->prev = prev;
00188 }
00189 
00190 static void cacheobj_free(struct cacheobj *cobj)
00191 {
00192     av_unref_obj(cobj->obj);
00193     av_log(AVLOG_DEBUG, "got rid of cached object <%s> size %lli",
00194              cobj->name != NULL ? cobj->name : "?", cobj->diskusage);
00195     av_free(cobj->name);
00196 }
00197 
00202 static void cacheobj_delete(struct cacheobj *cobj)
00203 {
00204     AV_LOCK(cachelock);
00205     if(cobj->obj != NULL) {
00206         cacheobj_remove(cobj);
00207         disk_usage -= cobj->diskusage;
00208     }
00209     AV_UNLOCK(cachelock);
00210 
00211     if(cobj->obj != NULL)
00212         cacheobj_free(cobj);
00213 }
00214 
00221 static void cacheobj_internal_delete(struct cacheobj *cobj)
00222 {
00223     if(cobj->obj != NULL) {
00224         cacheobj_remove(cobj);
00225         disk_usage -= cobj->diskusage;
00226     }
00227 
00228     AV_UNLOCK(cachelock);
00229     if(cobj->obj != NULL)
00230         cacheobj_free(cobj);
00231     AV_LOCK(cachelock);
00232 }
00233 
00234 struct cacheobj *av_cacheobj_new(void *obj, const char *name)
00235 {
00236     struct cacheobj *cobj;
00237 
00238     if(obj == NULL)
00239         return NULL;
00240 
00241     AV_NEW_OBJ(cobj, cacheobj_delete);
00242     cobj->obj = obj;
00243     cobj->diskusage = 0;
00244     cobj->name = av_strdup(name);
00245     cobj->internal_obj = 0;
00246     av_ref_obj(obj);
00247 
00248     AV_LOCK(cachelock);
00249     cacheobj_insert(cobj);
00250     AV_UNLOCK(cachelock);
00251 
00252     return cobj;
00253 }
00254 
00255 static int cache_free_one(struct cacheobj *skip_entry)
00256 {
00257     struct cacheobj *cobj;
00258     struct cacheobj tmpcobj;
00259 
00260     cobj = cachelist.prev;
00261     if(cobj == skip_entry)
00262        cobj = cobj->prev;
00263     if(cobj == &cachelist)
00264         return 0;
00265 
00266     if(cobj->internal_obj) {
00267         av_unref_obj(cobj);
00268     } else {
00269         cacheobj_remove(cobj);
00270         disk_usage -= cobj->diskusage;
00271         tmpcobj = *cobj;
00272         cobj->obj = NULL;
00273         AV_UNLOCK(cachelock);
00274         cacheobj_free(&tmpcobj);
00275         AV_LOCK(cachelock);
00276     }
00277 
00278     return 1;
00279 }
00280 
00281 static int cache_clear()
00282 {
00283     AV_LOCK(cachelock);
00284     while(cache_free_one(NULL));
00285     AV_UNLOCK(cachelock);
00286     
00287     return 0;
00288 }
00289 
00290 static void cache_checkspace(int full, struct cacheobj *skip_entry)
00291 {
00292     avoff_t tmpfree;
00293     avoff_t limit;
00294     avoff_t keepfree;
00295     
00296     if(full)
00297         tmpfree = 0;
00298     else
00299         tmpfree = av_tmp_free();
00300 
00301     /* If free space can't be determined, then it is taken to be infinite */
00302     if(tmpfree == -1)
00303         tmpfree = AV_MAXOFF;
00304     
00305     keepfree = disk_keep_free;
00306     if(keepfree < 100 * 1024)
00307         keepfree = 100 * 1024;
00308 
00309     limit = disk_usage - disk_keep_free + tmpfree;
00310     if(disk_cache_limit < limit)
00311         limit = disk_cache_limit;
00312     
00313     while(disk_usage > limit)
00314         if(!cache_free_one(skip_entry))
00315             break;        
00316 }
00317 
00318 
00319 void av_cache_checkspace()
00320 {
00321     AV_LOCK(cachelock);
00322     cache_checkspace(0,NULL);
00323     AV_UNLOCK(cachelock);
00324 }
00325 
00326 void av_cache_diskfull()
00327 {
00328     AV_LOCK(cachelock);
00329     cache_checkspace(1,NULL);
00330     AV_UNLOCK(cachelock);
00331 }
00332 
00333 
00334 void av_cacheobj_setsize(struct cacheobj *cobj, avoff_t diskusage)
00335 {
00336     AV_LOCK(cachelock);
00337     if(cobj->obj != NULL && cobj->diskusage != diskusage) {
00338         disk_usage -= cobj->diskusage;
00339         cobj->diskusage = diskusage;
00340         disk_usage += cobj->diskusage;
00341         
00342         cache_checkspace(0, cobj);
00343     }
00344     AV_UNLOCK(cachelock);
00345 }
00346 
00347 void *av_cacheobj_get(struct cacheobj *cobj)
00348 {
00349     void *obj;
00350 
00351     if(cobj == NULL)
00352         return NULL;
00353 
00354     AV_LOCK(cachelock);
00355     obj = cobj->obj;
00356     if(obj != NULL) {
00357         cacheobj_remove(cobj);
00358         cacheobj_insert(cobj);
00359         av_ref_obj(obj);
00360     }
00361     AV_UNLOCK(cachelock);
00362 
00363     return obj;
00364 }
00365 
00366 static struct cacheobj *cacheobj2_find(const char *name)
00367 {
00368     struct cacheobj *cobj;
00369     
00370     for(cobj = cachelist.next; cobj != &cachelist; cobj = cobj->next) {
00371         if(cobj->internal_obj == 1)
00372             if(strcmp(cobj->name, name) == 0)
00373                 break;
00374     }
00375 
00376     if(cobj->obj == NULL)
00377         return NULL;
00378 
00379     return cobj;
00380 }
00381 
00382 int av_cache2_set(void *obj, const char *name)
00383 {
00384     struct cacheobj *cobj, *oldcobj;
00385 
00386     if(obj != NULL) {
00387         AV_NEW_OBJ(cobj, cacheobj_internal_delete);
00388         cobj->obj = obj;
00389         cobj->diskusage = 0;
00390         cobj->name = av_strdup(name);
00391         cobj->internal_obj = 1;
00392         av_ref_obj(obj);
00393     } else {
00394         cobj = NULL;
00395     }
00396 
00397     AV_LOCK(cachelock);
00398     oldcobj = cacheobj2_find(name);
00399 
00400     if(oldcobj != NULL )
00401         av_unref_obj(oldcobj);
00402 
00403     if(cobj != NULL)
00404         cacheobj_insert(cobj);
00405 
00406     AV_UNLOCK(cachelock);
00407 
00408     return 0;
00409 }
00410 
00411 void *av_cache2_get(const char *name)
00412 {
00413     struct cacheobj *cobj;
00414     void *obj = NULL;
00415     
00416     AV_LOCK(cachelock);
00417     cobj = cacheobj2_find(name);
00418     if(cobj != NULL) {
00419         cacheobj_remove(cobj);
00420         cacheobj_insert(cobj);
00421         obj = cobj->obj;
00422         av_ref_obj(obj);
00423     }
00424     AV_UNLOCK(cachelock);
00425 
00426     return obj;
00427 }
00428 
00429 void av_cache2_setsize(const char *name, avoff_t diskusage)
00430 {
00431     struct cacheobj *cobj;
00432 
00433     AV_LOCK(cachelock);
00434     cobj = cacheobj2_find(name);
00435     if(cobj->obj != NULL && cobj->diskusage != diskusage) {
00436         disk_usage -= cobj->diskusage;
00437         cobj->diskusage = diskusage;
00438         disk_usage += cobj->diskusage;
00439         
00440         cache_checkspace(0, cobj);
00441     }
00442     AV_UNLOCK(cachelock);
00443 }