Example #1
0
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;
}
Example #2
0
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);
}
Example #3
0
/* 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;
}
Example #4
0
/* 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");
}
Example #5
0
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");
}