Permalink
Browse files

Add cache manager to manage cache size and remove old entries

  • Loading branch information...
1 parent 43b7a1c commit a60c4e50d37f4fd2f88ae3a6fa8ce20a2948568c @paulcbetts committed Nov 10, 2008
Showing with 319 additions and 10 deletions.
  1. +4 −2 src/Makefile.am
  2. +235 −0 src/cachemgr.c
  3. +39 −0 src/cachemgr.h
  4. +2 −1 src/queue.c
  5. +1 −1 src/queue.h
  6. +3 −0 src/stdafx.h
  7. +25 −2 src/vcachefs.c
  8. +10 −4 src/vcachefs.h
View
@@ -10,9 +10,11 @@ bin_PROGRAMS = vcachefs
AM_CFLAGS = -std=c99 -g -O0
## HACK HACK HACK: We're adding libevent here, without ever actually checking for it!
-vcachefs_LDADD = $(VCACHEFS_LIBS) -lgthread-2.0 -lpthread -levent
+vcachefs_LDADD = $(VCACHEFS_LIBS) -lgthread-2.0 -lpthread \
+ #-levent
vcachefs_SOURCES = \
vcachefs.c \
stats.c \
- queue.c
+ queue.c \
+ cachemgr.c
View
@@ -0,0 +1,235 @@
+/*
+ * cachemgr.c - Cache size manager
+ *
+ * Copyright 2008 Paul Betts <paul.betts@gmail.com>
+ *
+ *
+ * License:
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "stdafx.h"
+#include "stats.h"
+#include "cachemgr.h"
+
+struct CacheManager {
+ char* cache_root;
+
+ CMCanDeleteCallback can_delete_callback;
+ gpointer user_context;
+
+ GSList* file_list;
+ GStaticRWLock file_list_rwlock;
+};
+
+struct CacheItem {
+ char* path;
+ time_t mtime;
+ off_t size;
+};
+
+static struct CacheItem* cacheitem_new(const char* full_path)
+{
+ /* We will only return a new item if this is a valid path, and not
+ * something other than a file */
+ struct stat st;
+ if(lstat(full_path, &st) != 0 || !S_ISREG(st.st_mode))
+ return NULL;
+
+ struct CacheItem* ret = g_new0(struct CacheItem, 1);
+ ret->path = g_strdup(full_path);
+ ret->mtime = st.st_mtime;
+ ret->size = st.st_size;
+ return ret;
+}
+
+static void cacheitem_free(struct CacheItem* obj)
+{
+ if (!obj)
+ return;
+
+ g_free(obj->path);
+ g_free(obj);
+}
+
+static void cacheitem_free_list(GSList* to_free)
+{
+ if (!to_free)
+ return;
+
+ GSList* iter = to_free;
+ while (iter) {
+ if (iter->data)
+ cacheitem_free(iter->data);
+ iter = g_slist_next(iter);
+ }
+ g_slist_free(to_free);
+}
+
+static gint cache_item_sortfunc(gconstpointer lhs, gconstpointer rhs)
+{
+ time_t lhs_t = ((struct CacheItem*)lhs)->mtime;
+ time_t rhs_t = ((struct CacheItem*)rhs)->mtime;
+
+ if (lhs_t == rhs_t)
+ return 0;
+ return (lhs_t < rhs_t ? -1 : 1);
+}
+
+static void rebuild_cacheitem_list_from_root_helper(GSList** list, const char* root_path, GDir* root)
+{
+ const gchar* entry = NULL;
+ while ( (entry = g_dir_read_name(root)) ) {
+ gchar* full_path = g_build_filename(root_path, entry, NULL);
+
+ /* If this is a file in the cache, add it, sorted by mtime */
+ struct CacheItem* item = cacheitem_new(full_path);
+ if (item) {
+ *list = g_slist_insert_sorted(*list, item, cache_item_sortfunc);
+ goto done;
+ }
+
+ /* If this is a directory, recurse through it */
+ GDir* subdir = NULL;
+ if ( (subdir = g_dir_open(full_path, 0, NULL)) ) {
+ rebuild_cacheitem_list_from_root_helper(list, full_path, subdir);
+ g_dir_close(subdir);
+ }
+done:
+ g_free(full_path);
+ }
+}
+
+static void rebuild_cacheitem_list_from_root(struct CacheManager* this, const char* root_path)
+{
+ GSList* ret = NULL;
+ GDir* root = g_dir_open(root_path, 0, NULL);
+ if (!root)
+ return;
+
+ rebuild_cacheitem_list_from_root_helper(&ret, root_path, root);
+ g_dir_close(root);
+
+ /* Switch out the list and trash the old one */
+ g_static_rw_lock_writer_lock(&this->file_list_rwlock);
+ GSList* to_free = this->file_list;
+ this->file_list = ret;
+ g_static_rw_lock_writer_unlock(&this->file_list_rwlock);
+
+ cacheitem_free_list(to_free);
+}
+
+struct CacheManager* cache_manager_new(const char* cache_root, CMCanDeleteCallback callback, gpointer context)
+{
+ struct CacheManager* ret = g_new0(struct CacheManager, 1);
+ if (!ret)
+ goto failed;
+ ret->cache_root = g_strdup(cache_root);
+ ret->can_delete_callback = callback; ret->user_context = context;
+
+ g_static_rw_lock_init(&ret->file_list_rwlock);
+
+ rebuild_cacheitem_list_from_root(ret, cache_root);
+
+ return ret;
+
+failed:
+ if (ret) {
+ if (ret->cache_root)
+ g_free(ret->cache_root);
+ g_free(ret);
+ }
+ return NULL;
+}
+
+void cache_manager_free(struct CacheManager* obj)
+{
+ if (!obj)
+ return;
+
+ cacheitem_free_list(obj->file_list);
+ g_free(obj->cache_root);
+ g_free(obj);
+}
+
+guint64 cache_manager_get_size(struct CacheManager* this)
+{
+ if (!this)
+ return 0;
+
+ /* Run through the list and sum the sizes */
+ guint64 ret = 0;
+ g_static_rw_lock_reader_lock(&this->file_list_rwlock);
+ GSList* iter = this->file_list;
+ while (iter) {
+ ret += ((struct CacheItem*)iter->data)->size;
+ iter = g_slist_next(iter);
+ }
+ g_static_rw_lock_reader_unlock(&this->file_list_rwlock);
+
+ return ret;
+}
+
+void cache_manager_notify_added(struct CacheManager* this, const char* full_path)
+{
+ struct CacheItem* item = NULL;
+ if (! (item = cacheitem_new(full_path)) )
+ return;
+
+ g_static_rw_lock_writer_lock(&this->file_list_rwlock);
+ this->file_list = g_slist_insert_sorted(this->file_list, item, cache_item_sortfunc);
+ g_static_rw_lock_writer_unlock(&this->file_list_rwlock);
+}
+
+guint64 cache_manager_reclaim_space(struct CacheManager* this, guint64 max_size)
+{
+ guint64 current_size = cache_manager_get_size(this);
+ if (current_size < max_size)
+ return 0;
+
+ GSList* remove_list = NULL;
+ guint64 removed_size = 0;
+
+ /* Iterate through the sorted list, looking for files we can delete */
+ g_static_rw_lock_reader_lock(&this->file_list_rwlock);
+ GSList* iter = this->file_list;
+ while (iter) {
+ struct CacheItem* item = iter->data;
+
+ if ( item && (this->can_delete_callback)(item->path, this->user_context) ) {
+ remove_list = g_slist_prepend(remove_list, item);
+ unlink(item->path);
+ removed_size += item->size;
+ }
+
+ iter = g_slist_next(iter);
+ }
+ g_static_rw_lock_reader_unlock(&this->file_list_rwlock);
+
+ /* Remove the list of deleted items from the cache manager and free
+ * the delete list */
+ g_static_rw_lock_writer_lock(&this->file_list_rwlock);
+ iter = remove_list;
+ while (iter) {
+ this->file_list = g_slist_remove(this->file_list, iter->data);
+ iter = g_slist_next(iter);
+ }
+ g_static_rw_lock_writer_unlock(&this->file_list_rwlock);
+
+ cacheitem_free_list(remove_list);
+
+ return removed_size;
+}
View
@@ -0,0 +1,39 @@
+/*
+ * cachemgr.h - Userspace video caching filesystem
+ *
+ * Copyright 2008 Paul Betts <paul.betts@gmail.com>
+ *
+ *
+ * License:
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _CACHEMGR_H
+#define _CACHEMGR_H
+
+#include "stdafx.h"
+#include "queue.h"
+
+typedef gboolean (*CMCanDeleteCallback) (const char* path, gpointer context);
+struct CacheManager;
+
+struct CacheManager* cache_manager_new(const char* cache_root, CMCanDeleteCallback callback, gpointer context);
+void cache_manager_free(struct CacheManager* obj);
+guint64 cache_manager_get_size(struct CacheManager* this);
+void cache_manager_notify_added(struct CacheManager* this, const char* full_path);
+guint64 cache_manager_reclaim_space(struct CacheManager* this, guint64 max_size);
+
+#endif
View
@@ -55,7 +55,7 @@ static gpointer worker_thread_proc(gpointer data)
return 0;
}
-struct WorkitemQueue* workitem_queue_init(void)
+struct WorkitemQueue* workitem_queue_new(void)
{
struct WorkitemQueue* ret = g_new0(struct WorkitemQueue, 1);
if (!ret)
@@ -87,6 +87,7 @@ void workitem_queue_free(struct WorkitemQueue* queue)
queue->should_quit = TRUE;
+ /* Clear out the action queue */
struct Workitem* to_free;
g_async_queue_lock(queue->to_process);
while( !(to_free = g_async_queue_try_pop_unlocked(queue->to_process)) ) {
View
@@ -29,7 +29,7 @@
struct WorkitemQueue;
-struct WorkitemQueue* workitem_queue_init(void);
+struct WorkitemQueue* workitem_queue_new(void);
void workitem_queue_free(struct WorkitemQueue* queue);
gboolean workitem_queue_insert(struct WorkitemQueue* queue, GFunc func, gpointer data, gpointer context);
View
@@ -32,10 +32,13 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/uio.h>
+#include <sys/stat.h>
#include <sys/statvfs.h>
#include <dirent.h>
#include <unistd.h>
#include <glib.h>
+#include "config.h"
+
#endif
Oops, something went wrong.

0 comments on commit a60c4e5

Please sign in to comment.