aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-msgport.c
diff options
context:
space:
mode:
authorNot Zed <NotZed@Ximian.com>2004-01-14 13:04:04 +0800
committerMichael Zucci <zucchi@src.gnome.org>2004-01-14 13:04:04 +0800
commit18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808 (patch)
tree72e9a61666b1809b3ce21f30b6ec93d5806e6528 /e-util/e-msgport.c
parent7fdc703916b8d40398f16d205e953867015d2bac (diff)
downloadgsoc2013-evolution-18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808.tar
gsoc2013-evolution-18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808.tar.gz
gsoc2013-evolution-18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808.tar.bz2
gsoc2013-evolution-18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808.tar.lz
gsoc2013-evolution-18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808.tar.xz
gsoc2013-evolution-18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808.tar.zst
gsoc2013-evolution-18b15bfc5ae50a909621c7ea3e00e6f8ff7b4808.zip
A time-based thread-safe in-memory cache thing. Called em_cache 'cause
2004-01-13 Not Zed <NotZed@Ximian.com> * e-msgport.c (em_cache*): A time-based thread-safe in-memory cache thing. Called em_cache 'cause there's an e_cache in gal. svn path=/trunk/; revision=24213
Diffstat (limited to 'e-util/e-msgport.c')
-rw-r--r--e-util/e-msgport.c209
1 files changed, 209 insertions, 0 deletions
diff --git a/e-util/e-msgport.c b/e-util/e-msgport.c
index 36ea6cfe8d..f360a3f157 100644
--- a/e-util/e-msgport.c
+++ b/e-util/e-msgport.c
@@ -43,6 +43,7 @@
#define m(x) /* msgport debug */
#define t(x) /* thread debug */
+#define c(x) /* cache debug */
void e_dlist_init(EDList *v)
{
@@ -125,6 +126,214 @@ int e_dlist_length(EDList *l)
return count;
}
+struct _EMCache {
+ GMutex *lock;
+ GHashTable *key_table;
+ EDList lru_list;
+ size_t node_size;
+ int node_count;
+ time_t timeout;
+ GFreeFunc node_free;
+};
+
+/**
+ * em_cache_new:
+ * @timeout:
+ * @nodesize:
+ * @nodefree:
+ *
+ * Setup a new timeout cache. @nodesize is the size of nodes in the
+ * cache, and @nodefree will be called to free YOUR content.
+ *
+ * Return value:
+ **/
+EMCache *
+em_cache_new(time_t timeout, size_t nodesize, GFreeFunc nodefree)
+{
+ struct _EMCache *emc;
+
+ emc = g_malloc0(sizeof(*emc));
+ emc->node_size = nodesize;
+ emc->key_table = g_hash_table_new(g_str_hash, g_str_equal);
+ emc->node_free = nodefree;
+ e_dlist_init(&emc->lru_list);
+ emc->lock = g_mutex_new();
+ emc->timeout = timeout;
+
+ return emc;
+}
+
+/**
+ * em_cache_destroy:
+ * @emc:
+ *
+ * destroy the cache, duh.
+ **/
+void
+em_cache_destroy(EMCache *emc)
+{
+ em_cache_clear(emc);
+ g_mutex_free(emc->lock);
+ g_free(emc);
+}
+
+/**
+ * em_cache_lookup:
+ * @emc:
+ * @key:
+ *
+ * Lookup a cache node. once you're finished with it, you need to
+ * unref it.
+ *
+ * Return value:
+ **/
+EMCacheNode *
+em_cache_lookup(EMCache *emc, const char *key)
+{
+ EMCacheNode *n;
+
+ g_mutex_lock(emc->lock);
+ n = g_hash_table_lookup(emc->key_table, key);
+ if (n) {
+ e_dlist_remove((EDListNode *)n);
+ e_dlist_addhead(&emc->lru_list, (EDListNode *)n);
+ n->stamp = time(0);
+ n->ref_count++;
+ }
+ g_mutex_unlock(emc->lock);
+
+ c(printf("looking up '%s' %s\n", key, n?"found":"not found"));
+
+ return n;
+}
+
+/**
+ * em_cache_node_new:
+ * @emc:
+ * @key:
+ *
+ * Create a new key'd cache node. The node will not be added to the
+ * cache until you insert it.
+ *
+ * Return value:
+ **/
+EMCacheNode *
+em_cache_node_new(EMCache *emc, const char *key)
+{
+ EMCacheNode *n;
+
+ /* this could use memchunks, but its probably overkill */
+ n = g_malloc0(emc->node_size);
+ n->key = g_strdup(key);
+
+ return n;
+}
+
+/**
+ * em_cache_node_unref:
+ * @emc:
+ * @n:
+ *
+ * unref a cache node, you can only unref nodes which have been looked
+ * up.
+ **/
+void
+em_cache_node_unref(EMCache *emc, EMCacheNode *n)
+{
+ g_mutex_lock(emc->lock);
+ g_assert(n->ref_count > 0);
+ n->ref_count--;
+ g_mutex_unlock(emc->lock);
+}
+
+/**
+ * em_cache_add:
+ * @emc:
+ * @n:
+ *
+ * Add a cache node to the cache, once added the memory is owned by
+ * the cache. If there are conflicts and the old node is still in
+ * use, then the new node is not added, otherwise it is added and any
+ * nodes older than the expire time are flushed.
+ **/
+void
+em_cache_add(EMCache *emc, EMCacheNode *n)
+{
+ EMCacheNode *old, *prev;
+ EDList old_nodes;
+
+ e_dlist_init(&old_nodes);
+
+ g_mutex_lock(emc->lock);
+ old = g_hash_table_lookup(emc->key_table, n->key);
+ if (old != NULL) {
+ if (old->ref_count == 0) {
+ g_hash_table_remove(emc->key_table, old->key);
+ e_dlist_remove((EDListNode *)old);
+ e_dlist_addtail(&old_nodes, (EDListNode *)old);
+ goto insert;
+ } else {
+ e_dlist_addtail(&old_nodes, (EDListNode *)n);
+ }
+ } else {
+ time_t now;
+ insert:
+ now = time(0);
+ g_hash_table_insert(emc->key_table, n->key, n);
+ e_dlist_addhead(&emc->lru_list, (EDListNode *)n);
+ n->stamp = now;
+ emc->node_count++;
+
+ c(printf("inserting node %s\n", n->key));
+
+ old = (EMCacheNode *)emc->lru_list.tailpred;
+ prev = old->prev;
+ while (prev && old->stamp < now - emc->timeout) {
+ if (old->ref_count == 0) {
+ c(printf("expiring node %s\n", old->key));
+ g_hash_table_remove(emc->key_table, old->key);
+ e_dlist_remove((EDListNode *)old);
+ e_dlist_addtail(&old_nodes, (EDListNode *)old);
+ }
+ old = prev;
+ prev = prev->prev;
+ }
+ }
+
+ g_mutex_unlock(emc->lock);
+
+ while ((old = (EMCacheNode *)e_dlist_remhead(&old_nodes))) {
+ emc->node_free(old);
+ g_free(old->key);
+ g_free(old);
+ }
+}
+
+/**
+ * em_cache_clear:
+ * @emc:
+ *
+ * clear the cache. just for api completeness.
+ **/
+void
+em_cache_clear(EMCache *emc)
+{
+ EMCacheNode *node;
+ EDList old_nodes;
+
+ e_dlist_init(&old_nodes);
+ g_mutex_lock(emc->lock);
+ while ((node = (EMCacheNode *)e_dlist_remhead(&emc->lru_list)))
+ e_dlist_addtail(&old_nodes, (EDListNode *)node);
+ g_mutex_unlock(emc->lock);
+
+ while ((node = (EMCacheNode *)e_dlist_remhead(&old_nodes))) {
+ emc->node_free(node);
+ g_free(node->key);
+ g_free(node);
+ }
+}
+
struct _EMsgPort {
EDList queue;
int condwait; /* how many waiting in condwait */