diff --git a/src/cache.c b/src/cache.c index 66bdd0ce..1aeb9096 100644 --- a/src/cache.c +++ b/src/cache.c @@ -42,6 +42,7 @@ #include "httpd_daap.h" #include "db.h" #include "cache.h" +#include "listener.h" #include "commands.h" @@ -73,8 +74,8 @@ static pthread_t tid_cache; // Event base, pipes and events struct event_base *evbase_cache; -static struct event *g_cacheev; static struct commands_base *cmdbase; +static struct event *cache_daap_updateev; static int g_initialized; @@ -91,8 +92,6 @@ struct stash uint8_t *data; } g_stash; -// After being triggered wait 60 seconds before rebuilding cache -static struct timeval g_wait = { 60, 0 }; static int g_suspended; // The user may configure a threshold (in msec), and queries slower than @@ -608,6 +607,7 @@ cache_daap_query_add(void *arg, int *retval) #define Q_TMPL "INSERT OR REPLACE INTO queries (user_agent, query, msec, timestamp) VALUES ('%q', '%q', %d, %" PRIi64 ");" #define Q_CLEANUP "DELETE FROM queries WHERE id NOT IN (SELECT id FROM queries ORDER BY timestamp DESC LIMIT 20);" struct cache_arg *cmdarg; + struct timeval delay = { 60, 0 }; char *query; char *errmsg; int ret; @@ -663,7 +663,9 @@ cache_daap_query_add(void *arg, int *retval) return COMMAND_END; } - cache_daap_trigger(); + // Will set of cache regeneration after waiting a bit (so there is less risk + // of disturbing the user) + evtimer_add(cache_daap_updateev, &delay); *retval = 0; return COMMAND_END; @@ -790,7 +792,13 @@ cache_daap_update_cb(int fd, short what, void *arg) char *query; int ret; - DPRINTF(E_INFO, L_CACHE, "Timeout reached, time to update DAAP cache\n"); + if (g_suspended) + { + DPRINTF(E_DBG, L_CACHE, "Got a request to update DAAP cache while suspended\n"); + return; + } + + DPRINTF(E_LOG, L_CACHE, "Beginning DAAP cache update\n"); ret = sqlite3_exec(g_db_hdl, "DELETE FROM replies;", NULL, NULL, &errmsg); if (ret != SQLITE_OK) @@ -845,63 +853,28 @@ cache_daap_update_cb(int fd, short what, void *arg) sqlite3_finalize(stmt); - DPRINTF(E_INFO, L_CACHE, "DAAP cache updated\n"); + DPRINTF(E_LOG, L_CACHE, "DAAP cache updated\n"); } -/* This function will just set a timer, which when it times out will trigger - * the actual cache update. The purpose is to avoid avoid cache updates when - * the database is busy, eg during a library scan. +/* Sets off an update by activating the event. The delay is because we are low + * priority compared to other listeners of database updates. */ static enum command_state -cache_daap_update_timer(void *arg, int *ret) +cache_daap_update(void *arg, int *retval) { - if (!g_cacheev) - { - *ret = -1; - return COMMAND_END; - } - - evtimer_add(g_cacheev, &g_wait); - - *ret = 0; + struct timeval delay = { 10, 0 }; + *retval = event_add(cache_daap_updateev, &delay); return COMMAND_END; } -static enum command_state -cache_daap_suspend_timer(void *arg, int *ret) +/* Callback from filescanner thread */ +static void +cache_daap_listener_cb(enum listener_event_type type) { - if (!g_cacheev) - { - *ret = -1; - return COMMAND_END; - } - - g_suspended = evtimer_pending(g_cacheev, NULL); - if (g_suspended) - evtimer_del(g_cacheev); - - *ret = 0; - - return COMMAND_END; + commands_exec_async(cmdbase, cache_daap_update, NULL); } -static enum command_state -cache_daap_resume_timer(void *arg, int *ret) -{ - if (!g_cacheev) - { - *ret = -1; - return COMMAND_END; - } - - if (g_suspended) - evtimer_add(g_cacheev, &g_wait); - - *ret = 0; - - return COMMAND_END; -} /* * Updates cached timestamps to current time for all cache entries for the given path, if the file was not modfied @@ -1328,31 +1301,16 @@ cache(void *arg) * */ -void -cache_daap_trigger(void) -{ - if (!g_initialized) - return; - - commands_exec_async(cmdbase, cache_daap_update_timer, NULL); -} - void cache_daap_suspend(void) { - if (!g_initialized) - return; - - commands_exec_async(cmdbase, cache_daap_suspend_timer, NULL); + g_suspended = 1; } void cache_daap_resume(void) { - if (!g_initialized) - return; - - commands_exec_async(cmdbase, cache_daap_resume_timer, NULL); + g_suspended = 0; } int @@ -1377,15 +1335,13 @@ cache_daap_add(const char *query, const char *ua, int msec) if (!g_initialized) return; - cmdarg = (struct cache_arg *)malloc(sizeof(struct cache_arg)); + cmdarg = calloc(1, sizeof(struct cache_arg)); if (!cmdarg) { DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_arg\n"); return; } - memset(cmdarg, 0, sizeof(struct cache_arg)); - cmdarg->query = strdup(query); cmdarg->ua = strdup(ua); cmdarg->msec = msec; @@ -1422,15 +1378,13 @@ cache_artwork_ping(const char *path, time_t mtime, int del) if (!g_initialized) return; - cmdarg = (struct cache_arg *)malloc(sizeof(struct cache_arg)); + cmdarg = calloc(1, sizeof(struct cache_arg)); if (!cmdarg) { DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_arg\n"); return; } - memset(cmdarg, 0, sizeof(struct cache_arg)); - cmdarg->path = strdup(path); cmdarg->mtime = mtime; cmdarg->del = del; @@ -1629,8 +1583,8 @@ cache_init(void) goto evbase_fail; } - g_cacheev = evtimer_new(evbase_cache, cache_daap_update_cb, NULL); - if (!g_cacheev) + cache_daap_updateev = evtimer_new(evbase_cache, cache_daap_update_cb, NULL); + if (!cache_daap_updateev) { DPRINTF(E_LOG, L_CACHE, "Could not create cache event\n"); goto evnew_fail; @@ -1638,6 +1592,13 @@ cache_init(void) cmdbase = commands_base_new(evbase_cache, NULL); + ret = listener_add(cache_daap_listener_cb, LISTENER_DATABASE); + if (ret < 0) + { + DPRINTF(E_LOG, L_CACHE, "Could not create listener event\n"); + goto listener_fail; + } + DPRINTF(E_INFO, L_CACHE, "cache thread init\n"); ret = pthread_create(&tid_cache, NULL, cache, NULL); @@ -1657,6 +1618,8 @@ cache_init(void) return 0; thread_fail: + listener_remove(cache_daap_listener_cb); + listener_fail: commands_base_free(cmdbase); evnew_fail: event_base_free(evbase_cache); @@ -1675,6 +1638,9 @@ cache_deinit(void) return; g_initialized = 0; + + listener_remove(cache_daap_listener_cb); + commands_base_destroy(cmdbase); ret = pthread_join(tid_cache, NULL); diff --git a/src/cache.h b/src/cache.h index 7e4e112f..69acf4d0 100644 --- a/src/cache.h +++ b/src/cache.h @@ -6,9 +6,6 @@ /* ---------------------------- DAAP cache API --------------------------- */ -void -cache_daap_trigger(void); - void cache_daap_suspend(void); diff --git a/src/db.c b/src/db.c index 336d362c..7ce9db3f 100644 --- a/src/db.c +++ b/src/db.c @@ -41,6 +41,7 @@ #include "logger.h" #include "cache.h" #include "listener.h" +#include "filescanner.h" #include "misc.h" #include "db.h" #include "db_init.h" @@ -1446,8 +1447,15 @@ db_query_end(struct query_params *qp) qp->stmt = NULL; } +/* + * Utility function for running write queries (INSERT, UPDATE, DELETE). If you + * set free to non-zero, the function will free the query. If you set + * library_update to non-zero it means that the update was not just of some + * internal value (like a timestamp), but of something that requires clients + * to update their cache of the library (and of course also of our own cache). + */ static int -db_query_run(char *query, int free, int cache_update) +db_query_run(char *query, int free, int library_update) { char *errmsg; int ret; @@ -1473,10 +1481,10 @@ db_query_run(char *query, int free, int cache_update) if (free) sqlite3_free(query); - if (cache_update) - cache_daap_trigger(); - else - cache_daap_resume(); + cache_daap_resume(); + + if (library_update) + library_update_trigger(); return ((ret != SQLITE_OK) ? -1 : 0); } @@ -2404,7 +2412,7 @@ db_file_add(struct media_file_info *mfi) sqlite3_free(query); - cache_daap_trigger(); + library_update_trigger(); return 0; @@ -2483,7 +2491,7 @@ db_file_update(struct media_file_info *mfi) sqlite3_free(query); - cache_daap_trigger(); + library_update_trigger(); return 0; diff --git a/src/library/filescanner.c b/src/library/filescanner.c index f3e5e030..465345f1 100644 --- a/src/library/filescanner.c +++ b/src/library/filescanner.c @@ -61,6 +61,7 @@ #include "player.h" #include "cache.h" #include "artwork.h" +#include "listener.h" #include "commands.h" #include "library.h" @@ -107,13 +108,19 @@ struct stacked_dir { static int inofd; static struct event *inoev; +static struct event *updateev; static struct deferred_pl *playlists; static struct stacked_dir *dirstack; +// After being told by db that the library was updated through update_trigger(), +// wait 60 seconds before notifying listeners of LISTENER_DATABASE. This is to +// avoid bombarding the listeners while there are many db updates, and to make +// sure they only get a single update (useful for the cache). +static struct timeval library_update_wait = { 60, 0 }; + /* From library.c */ extern struct event_base *evbase_lib; - #ifndef __linux__ struct deferred_file { @@ -841,6 +848,8 @@ bulk_scan(int flags) { DPRINTF(E_LOG, L_SCAN, "Bulk library scan completed in %.f sec\n", difftime(end, start)); } + + listener_notify(LISTENER_DATABASE); // TODO Move to library.c } static int @@ -1469,6 +1478,21 @@ filescanner_initscan() return 0; } +static void +update_trigger_cb(int fd, short what, void *arg) +{ + listener_notify(LISTENER_DATABASE); +} + +static enum command_state +update_trigger(void *arg, int *retval) +{ + evtimer_add(updateev, &library_update_wait); + + *retval = 0; + return COMMAND_END; +} + static int filescanner_rescan() { @@ -1494,6 +1518,16 @@ filescanner_fullrescan() return 0; } +// TODO Move to abstraction +void +library_update_trigger(void) +{ + if (scanning) + return; + + commands_exec_async(cmdbase, update_trigger, NULL); +} + /* Thread: main */ static int filescanner_init(void) @@ -1501,8 +1535,20 @@ filescanner_init(void) int ret; ret = inofd_event_set(); + if (ret < 0) + { + return -1; + } - return ret; + updateev = evtimer_new(evbase_lib, update_trigger_cb, NULL); + if (!updateev) + { + DPRINTF(E_FATAL, L_SCAN, "Could not create library update event\n"); + close(inofd); + return -1; + } + + return 0; } /* Thread: main */ diff --git a/src/library/filescanner.h b/src/library/filescanner.h index 1acdf61e..c15c655c 100644 --- a/src/library/filescanner.h +++ b/src/library/filescanner.h @@ -31,4 +31,7 @@ void scan_itunes_itml(char *file); #endif +void +library_update_trigger(void); // TODO Move to library abstraction + #endif /* !__FILESCANNER_H__ */