static void *locked_read_add (struct tags_cache *c, const char *file, const int tags_sel, const int client_id, DBT *key, DBT *serialized_cache_rec) { int ret; struct file_tags *tags = NULL; assert (c->db != NULL); ret = c->db->get (c->db, NULL, key, serialized_cache_rec, 0); if (ret && ret != DB_NOTFOUND) logit ("Cache DB get error: %s", db_strerror (ret)); /* If this entry is already present in the cache, we have 3 options: * we must read different tags (TAGS_*) or the tags are outdated * or this is an immediate tags read (client_id == -1) */ if (ret == 0) { struct cache_record rec; if (cache_record_deserialize (&rec, serialized_cache_rec->data, serialized_cache_rec->size, 0)) { time_t curr_mtime = get_mtime (file); if (rec.mod_time != curr_mtime) { debug ("Tags in the cache are outdated"); tags_free (rec.tags); /* remove them and reread tags */ } else if ((rec.tags->filled & tags_sel) == tags_sel && client_id == -1) { debug ("Tags are in the cache."); return rec.tags; } else { debug ("Tags in the cache are not what we want"); tags = rec.tags; /* read additional tags */ } } } tags = read_missing_tags (file, tags, tags_sel); tags_cache_add (c, file, key, tags); return tags; }
void tags_cache_add_request (struct tags_cache *c, const char *file, const int tags_sel, const int client_id) { DBT serialized_cache_rec; DBT key; int db_ret; int got_lock; DB_LOCK lock; assert (c != NULL); assert (file != NULL); assert (client_id >= 0 && client_id < CLIENTS_MAX); debug ("Request for tags for %s from client %d", file, client_id); memset (&key, 0, sizeof(key)); key.data = (void *)file; key.size = strlen(file); memset (&serialized_cache_rec, 0, sizeof(serialized_cache_rec)); serialized_cache_rec.flags = DB_DBT_MALLOC; db_ret = c->db_env->lock_get (c->db_env, c->locker, 0, &key, DB_LOCK_WRITE, &lock); if (db_ret) { got_lock = 0; logit ("Can't get DB lock: %s", db_strerror(db_ret)); } else { got_lock = 1; } if (c->db) { db_ret = c->db->get(c->db, NULL, &key, &serialized_cache_rec, 0); if (db_ret && db_ret != DB_NOTFOUND) error ("Cache DB search error: %s", db_strerror(db_ret)); } else db_ret = DB_NOTFOUND; if (db_ret == 0) { struct cache_record rec; if (cache_record_deserialize(&rec, serialized_cache_rec.data, serialized_cache_rec.size, 0)) { if (rec.mod_time == get_mtime(file) && (rec.tags->filled & tags_sel) == tags_sel) { tags_response (client_id, file, rec.tags); tags_free (rec.tags); debug ("Tags are present in the cache"); goto end; } tags_free (rec.tags); debug ("Found outdated or incomplete tags in the cache"); } } LOCK (c->mutex); request_queue_add (&c->queues[client_id], file, tags_sel); pthread_cond_signal (&c->request_cond); UNLOCK (c->mutex); end: if (got_lock) { db_ret = c->db_env->lock_put (c->db_env, &lock); if (db_ret) logit ("Can't release DB lock: %s", db_strerror(db_ret)); } if (serialized_cache_rec.data) free (serialized_cache_rec.data); }
/* Read the selected tags for this file and add it to the cache. * If client_id != -1, the server is notified using tags_response(). * If client_id == -1, copy of file_tags is returned. */ static struct file_tags *tags_cache_read_add (struct tags_cache *c, const int client_id, const char *file, int tags_sel) { struct file_tags *tags = NULL; DBT key; DBT serialized_cache_rec; DB_LOCK lock; int got_lock = 0; int ret; assert (c != NULL); assert (c->db != NULL); assert (file != NULL); debug ("Getting tags for %s", file); memset (&key, 0, sizeof(key)); memset (&serialized_cache_rec, 0, sizeof(serialized_cache_rec)); key.data = (void *)file; key.size = strlen(file); serialized_cache_rec.flags = DB_DBT_MALLOC; ret = c->db_env->lock_get (c->db_env, c->locker, 0, &key, DB_LOCK_WRITE, &lock); if (ret) { logit ("Can't get DB lock: %s", db_strerror(ret)); } else { got_lock = 1; ret = c->db->get (c->db, NULL, &key, &serialized_cache_rec, 0); if (ret && ret != DB_NOTFOUND) logit ("Cache DB get error: %s", db_strerror(ret)); } /* If this entry is already present in the cache, we have 3 options: * we must read different tags (TAGS_*) or the tags are outdated * or this is an immediate tags read (client_id == -1) */ if (ret == 0) { struct cache_record rec; if (cache_record_deserialize (&rec, serialized_cache_rec.data, serialized_cache_rec.size, 0)) { time_t curr_mtime = get_mtime (file); if (rec.mod_time != curr_mtime) { /* outdated tags - remove them and reread */ tags_free (rec.tags); debug ("Tags in the cache are outdated"); } else if ((rec.tags->filled & tags_sel) == tags_sel && client_id == -1) { debug ("Tags are in the cache."); tags = rec.tags; goto end; } else { tags = rec.tags; /* read tags in addition to already present tags */ debug ("Tags in the cache are not what we want."); } } } if (tags == NULL) tags = tags_new (); if (tags_sel & TAGS_TIME) { int time; /* Try to get it from the server's playlist first. */ time = audio_get_ftime (file); if (time != -1) { tags->time = time; tags->filled |= TAGS_TIME; tags_sel &= ~TAGS_TIME; } } tags = read_file_tags (file, tags, tags_sel); debug ("Adding/updating cache object"); tags_cache_add (c, file, tags); if (client_id != -1) { tags_response (client_id, file, tags); tags_free (tags); tags = NULL; } /* TODO: Remove the oldest items from the cache if we exceeded the maximum * cache size */ end: if (got_lock) { ret = c->db_env->lock_put (c->db_env, &lock); if (ret) logit ("Can't release DB lock: %s", db_strerror(ret)); } if (serialized_cache_rec.data) free (serialized_cache_rec.data); return tags; }
/* Remove the one element of the cache based on it's access time. */ static void tags_cache_gc (struct tags_cache *c) { DBC *cur; DBT key; DBT serialized_cache_rec; int ret; char *last_referenced = NULL; time_t last_referenced_atime = time (NULL); int nitems = 0; assert (c != NULL); if (!c->db) return; c->db->cursor (c->db, NULL, &cur, 0); memset (&key, 0, sizeof(key)); memset (&serialized_cache_rec, 0, sizeof(serialized_cache_rec)); key.flags = DB_DBT_MALLOC; serialized_cache_rec.flags = DB_DBT_MALLOC; while ((ret = cur->c_get(cur, &key, &serialized_cache_rec, DB_NEXT)) == 0) { struct cache_record rec; if (cache_record_deserialize(&rec, serialized_cache_rec.data, serialized_cache_rec.size, 1) && rec.atime < last_referenced_atime) { last_referenced_atime = rec.atime; if (last_referenced) free (last_referenced); last_referenced = (char *)xmalloc (key.size + 1); memcpy (last_referenced, key.data, key.size); last_referenced[key.size] = '\0'; } // TODO: remove objects with serialization error. nitems++; free (key.data); free (serialized_cache_rec.data); } if (ret != DB_NOTFOUND) logit ("Searching for element to remove failed (cursor): %s", db_strerror(ret)); cur->c_close (cur); debug ("Elements in cache: %d (limit %d)", nitems, c->max_items); if (last_referenced) { if (nitems > c->max_items) tags_cache_remove_rec (c, last_referenced); free (last_referenced); } else debug ("Cache empty"); }
static void tags_cache_gc (struct tags_cache *c) { DBC *cur; DBT key; DBT serialized_cache_rec; int ret; char *last_referenced = NULL; time_t last_referenced_atime = time (NULL) + 1; int nitems = 0; c->db->cursor (c->db, NULL, &cur, 0); memset (&key, 0, sizeof(key)); memset (&serialized_cache_rec, 0, sizeof(serialized_cache_rec)); key.flags = DB_DBT_MALLOC; serialized_cache_rec.flags = DB_DBT_MALLOC; while (true) { struct cache_record rec; #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 6 ret = cur->c_get (cur, &key, &serialized_cache_rec, DB_NEXT); #else ret = cur->get (cur, &key, &serialized_cache_rec, DB_NEXT); #endif if (ret != 0) break; if (cache_record_deserialize (&rec, serialized_cache_rec.data, serialized_cache_rec.size, 1) && rec.atime < last_referenced_atime) { last_referenced_atime = rec.atime; if (last_referenced) free (last_referenced); last_referenced = (char *)xmalloc (key.size + 1); memcpy (last_referenced, key.data, key.size); last_referenced[key.size] = '\0'; } // TODO: remove objects with serialization error. nitems++; free (key.data); free (serialized_cache_rec.data); } if (ret != DB_NOTFOUND) log_errno ("Searching for element to remove failed (cursor)", ret); #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 6 cur->c_close (cur); #else cur->close (cur); #endif debug ("Elements in cache: %d (limit %d)", nitems, c->max_items); if (last_referenced) { if (nitems >= c->max_items) tags_cache_remove_rec (c, last_referenced); free (last_referenced); } else debug ("Cache empty"); }