Leave cache files mapped permanently.
Without reference counting on cache objects, there's no way to know when an application is finished using objects pulled from the cache. Until some kinf of cache reference counting can be done, leave all cache objects mapped for the life of the library (until FcFini is called). To mitigate the cost of this, ensure that each instance of a cache file is mapped only once.
This commit is contained in:
parent
9a9fd975a1
commit
8fe2104a1e
143
src/fccache.c
143
src/fccache.c
|
@ -134,7 +134,7 @@ FcDirCacheOpenFile (const FcChar8 *cache_file, struct stat *file_stat)
|
||||||
*/
|
*/
|
||||||
static FcBool
|
static FcBool
|
||||||
FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
|
FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
|
||||||
FcBool (*callback) (int fd, off_t size, void *closure),
|
FcBool (*callback) (int fd, struct stat *stat, void *closure),
|
||||||
void *closure, FcChar8 **cache_file_ret)
|
void *closure, FcChar8 **cache_file_ret)
|
||||||
{
|
{
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
@ -162,7 +162,7 @@ FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
if (dir_stat.st_mtime <= file_stat.st_mtime)
|
if (dir_stat.st_mtime <= file_stat.st_mtime)
|
||||||
{
|
{
|
||||||
ret = (*callback) (fd, file_stat.st_size, closure);
|
ret = (*callback) (fd, &file_stat, closure);
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
if (cache_file_ret)
|
if (cache_file_ret)
|
||||||
|
@ -182,26 +182,120 @@ FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FcDirCacheDispose (FcCache *cache)
|
||||||
|
{
|
||||||
|
switch (cache->magic) {
|
||||||
|
case FC_CACHE_MAGIC_ALLOC:
|
||||||
|
free (cache);
|
||||||
|
break;
|
||||||
|
case FC_CACHE_MAGIC_MMAP:
|
||||||
|
#if defined(HAVE_MMAP) || defined(__CYGWIN__)
|
||||||
|
munmap (cache, cache->size);
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
UnmapViewOfFile (cache);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define FC_CACHE_MIN_MMAP 1024
|
#define FC_CACHE_MIN_MMAP 1024
|
||||||
|
|
||||||
|
#define FC_CACHE_HASH_SIZE 67
|
||||||
|
|
||||||
|
typedef struct _FcCacheBucket FcCacheBucket;
|
||||||
|
|
||||||
|
struct _FcCacheBucket {
|
||||||
|
FcCacheBucket *next;
|
||||||
|
FcCache *cache;
|
||||||
|
dev_t cache_dev;
|
||||||
|
ino_t cache_ino;
|
||||||
|
time_t cache_mtime;
|
||||||
|
};
|
||||||
|
|
||||||
|
static FcCacheBucket *FcCacheBuckets[FC_CACHE_HASH_SIZE];
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
FcCacheStatHash (struct stat *cache_stat)
|
||||||
|
{
|
||||||
|
return ((uint32_t) cache_stat->st_dev ^
|
||||||
|
(uint32_t) cache_stat->st_ino ^
|
||||||
|
(uint32_t) cache_stat->st_mtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FcCache *
|
||||||
|
FcCacheLookup (struct stat *cache_stat)
|
||||||
|
{
|
||||||
|
uint32_t hash = FcCacheStatHash(cache_stat);
|
||||||
|
FcCacheBucket **bucket = &FcCacheBuckets[hash % FC_CACHE_HASH_SIZE];
|
||||||
|
FcCacheBucket *b;
|
||||||
|
|
||||||
|
for (b = *bucket; b; b = b->next)
|
||||||
|
if (b->cache_dev == cache_stat->st_dev &&
|
||||||
|
b->cache_ino == cache_stat->st_ino &&
|
||||||
|
b->cache_mtime == cache_stat->st_mtime)
|
||||||
|
return b->cache;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FcBool
|
||||||
|
FcCacheRecord (FcCache *cache, struct stat *cache_stat)
|
||||||
|
{
|
||||||
|
uint32_t hash = FcCacheStatHash(cache_stat);
|
||||||
|
FcCacheBucket **bucket = &FcCacheBuckets[hash % FC_CACHE_HASH_SIZE];
|
||||||
|
FcCacheBucket *b;
|
||||||
|
|
||||||
|
b = malloc (sizeof (FcCacheBucket));
|
||||||
|
if (!b)
|
||||||
|
return FcFalse;
|
||||||
|
b->next = *bucket;
|
||||||
|
b->cache = cache;
|
||||||
|
b->cache_dev = cache_stat->st_dev;
|
||||||
|
b->cache_ino = cache_stat->st_ino;
|
||||||
|
b->cache_mtime = cache_stat->st_mtime;
|
||||||
|
*bucket = b;
|
||||||
|
return FcTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FcCacheFini (void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
FcCacheBucket *b, *next;
|
||||||
|
|
||||||
|
for (i = 0; i < FC_CACHE_HASH_SIZE; i++)
|
||||||
|
{
|
||||||
|
for (b = FcCacheBuckets[i]; b; b = next)
|
||||||
|
{
|
||||||
|
next = b->next;
|
||||||
|
FcDirCacheDispose (b->cache);
|
||||||
|
free (b);
|
||||||
|
}
|
||||||
|
FcCacheBuckets[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Map a cache file into memory
|
* Map a cache file into memory
|
||||||
*/
|
*/
|
||||||
static FcCache *
|
static FcCache *
|
||||||
FcDirCacheMapFd (int fd, off_t size)
|
FcDirCacheMapFd (int fd, struct stat *fd_stat)
|
||||||
{
|
{
|
||||||
FcCache *cache = NULL;
|
FcCache *cache;
|
||||||
FcBool allocated = FcFalse;
|
FcBool allocated = FcFalse;
|
||||||
|
|
||||||
if (size < sizeof (FcCache))
|
if (fd_stat->st_size < sizeof (FcCache))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
cache = FcCacheLookup (fd_stat);
|
||||||
|
if (cache)
|
||||||
|
return cache;
|
||||||
/*
|
/*
|
||||||
* For small cache files, just read them into memory
|
* For small cache files, just read them into memory
|
||||||
*/
|
*/
|
||||||
if (size >= FC_CACHE_MIN_MMAP)
|
if (fd_stat->st_size >= FC_CACHE_MIN_MMAP)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_MMAP) || defined(__CYGWIN__)
|
#if defined(HAVE_MMAP) || defined(__CYGWIN__)
|
||||||
cache = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0);
|
cache = mmap (0, fd_stat->st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
{
|
{
|
||||||
HANDLE hFileMap;
|
HANDLE hFileMap;
|
||||||
|
@ -219,11 +313,11 @@ FcDirCacheMapFd (int fd, off_t size)
|
||||||
}
|
}
|
||||||
if (!cache)
|
if (!cache)
|
||||||
{
|
{
|
||||||
cache = malloc (size);
|
cache = malloc (fd_stat->st_size);
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (read (fd, cache, size) != size)
|
if (read (fd, cache, fd_stat->st_size) != fd_stat->st_size)
|
||||||
{
|
{
|
||||||
free (cache);
|
free (cache);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -232,14 +326,15 @@ FcDirCacheMapFd (int fd, off_t size)
|
||||||
}
|
}
|
||||||
if (cache->magic != FC_CACHE_MAGIC_MMAP ||
|
if (cache->magic != FC_CACHE_MAGIC_MMAP ||
|
||||||
cache->version < FC_CACHE_CONTENT_VERSION ||
|
cache->version < FC_CACHE_CONTENT_VERSION ||
|
||||||
cache->size != size)
|
cache->size != fd_stat->st_size ||
|
||||||
|
!FcCacheRecord (cache, fd_stat))
|
||||||
{
|
{
|
||||||
if (allocated)
|
if (allocated)
|
||||||
free (cache);
|
free (cache);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if defined(HAVE_MMAP) || defined(__CYGWIN__)
|
#if defined(HAVE_MMAP) || defined(__CYGWIN__)
|
||||||
munmap (cache, size);
|
munmap (cache, fd_stat->st_size);
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
UnmapViewOfFile (cache);
|
UnmapViewOfFile (cache);
|
||||||
#endif
|
#endif
|
||||||
|
@ -257,24 +352,13 @@ FcDirCacheMapFd (int fd, off_t size)
|
||||||
void
|
void
|
||||||
FcDirCacheUnload (FcCache *cache)
|
FcDirCacheUnload (FcCache *cache)
|
||||||
{
|
{
|
||||||
switch (cache->magic) {
|
/* Can't free or unmap the cache as apps may point into it */
|
||||||
case FC_CACHE_MAGIC_ALLOC:
|
|
||||||
free (cache);
|
|
||||||
break;
|
|
||||||
case FC_CACHE_MAGIC_MMAP:
|
|
||||||
#if defined(HAVE_MMAP) || defined(__CYGWIN__)
|
|
||||||
munmap (cache, cache->size);
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
UnmapViewOfFile (cache);
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static FcBool
|
static FcBool
|
||||||
FcDirCacheMapHelper (int fd, off_t size, void *closure)
|
FcDirCacheMapHelper (int fd, struct stat *fd_stat, void *closure)
|
||||||
{
|
{
|
||||||
FcCache *cache = FcDirCacheMapFd (fd, size);
|
FcCache *cache = FcDirCacheMapFd (fd, fd_stat);
|
||||||
|
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
@ -303,7 +387,7 @@ FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
|
||||||
fd = FcDirCacheOpenFile (cache_file, file_stat);
|
fd = FcDirCacheOpenFile (cache_file, file_stat);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
cache = FcDirCacheMapFd (fd, file_stat->st_size);
|
cache = FcDirCacheMapFd (fd, file_stat);
|
||||||
close (fd);
|
close (fd);
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
@ -313,11 +397,10 @@ FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
|
||||||
* the magic number and the size field
|
* the magic number and the size field
|
||||||
*/
|
*/
|
||||||
static FcBool
|
static FcBool
|
||||||
FcDirCacheValidateHelper (int fd, off_t size, void *closure)
|
FcDirCacheValidateHelper (int fd, struct stat *fd_stat, void *closure)
|
||||||
{
|
{
|
||||||
FcBool ret = FcTrue;
|
FcBool ret = FcTrue;
|
||||||
FcCache c;
|
FcCache c;
|
||||||
struct stat file_stat;
|
|
||||||
|
|
||||||
if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache))
|
if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache))
|
||||||
ret = FcFalse;
|
ret = FcFalse;
|
||||||
|
@ -325,9 +408,7 @@ FcDirCacheValidateHelper (int fd, off_t size, void *closure)
|
||||||
ret = FcFalse;
|
ret = FcFalse;
|
||||||
else if (c.version < FC_CACHE_CONTENT_VERSION)
|
else if (c.version < FC_CACHE_CONTENT_VERSION)
|
||||||
ret = FcFalse;
|
ret = FcFalse;
|
||||||
else if (fstat (fd, &file_stat) < 0)
|
else if (fd_stat->st_size != c.size)
|
||||||
ret = FcFalse;
|
|
||||||
else if (file_stat.st_size != c.size)
|
|
||||||
ret = FcFalse;
|
ret = FcFalse;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,6 +121,7 @@ FcFini (void)
|
||||||
FcConfigDestroy (_fcConfig);
|
FcConfigDestroy (_fcConfig);
|
||||||
|
|
||||||
FcPatternFini ();
|
FcPatternFini ();
|
||||||
|
FcCacheFini ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -514,6 +514,9 @@ FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, FcStrSet *dirs);
|
||||||
FcPrivate FcBool
|
FcPrivate FcBool
|
||||||
FcDirCacheWrite (FcCache *cache, FcConfig *config);
|
FcDirCacheWrite (FcCache *cache, FcConfig *config);
|
||||||
|
|
||||||
|
FcPrivate void
|
||||||
|
FcCacheFini (void);
|
||||||
|
|
||||||
/* fccfg.c */
|
/* fccfg.c */
|
||||||
|
|
||||||
FcPrivate FcBool
|
FcPrivate FcBool
|
||||||
|
|
Loading…
Reference in New Issue