diff --git a/src/Makefile.am b/src/Makefile.am index 32fe6011..3b40dfe8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -109,7 +109,8 @@ forked_daapd_SOURCES = main.c \ $(SPOTIFY_SRC) \ $(LASTFM_SRC) \ $(MPD_SRC) \ - listener.c listener.h + listener.c listener.h \ + commands.c commands.h nodist_forked_daapd_SOURCES = \ $(ANTLR_SOURCES) diff --git a/src/cache.c b/src/cache.c index 79e2244f..be77ab7f 100644 --- a/src/cache.c +++ b/src/cache.c @@ -41,42 +41,29 @@ #include "httpd_daap.h" #include "db.h" #include "cache.h" +#include "commands.h" #define CACHE_VERSION 2 -struct cache_command; -typedef int (*cmd_func)(struct cache_command *cmd); - -struct cache_command +struct cache_arg { - pthread_mutex_t lck; - pthread_cond_t cond; + char *query; // daap query + char *ua; // user agent + int msec; - cmd_func func; + char *path; // artwork path + int type; // individual or group artwork + int64_t persistentid; + int max_w; + int max_h; + int format; + time_t mtime; + int cached; + int del; - int nonblock; - - struct { - char *query; // daap query - char *ua; // user agent - int msec; - - char *path; // artwork path - int type; // individual or group artwork - int64_t persistentid; - int max_w; - int max_h; - int format; - time_t mtime; - int cached; - int del; - - struct evbuffer *evbuf; - } arg; - - int ret; + struct evbuffer *evbuf; }; /* --- Globals --- */ @@ -86,10 +73,9 @@ static pthread_t tid_cache; // Event base, pipes and events struct event_base *evbase_cache; static int g_exit_pipe[2]; -static int g_cmd_pipe[2]; static struct event *g_exitev; -static struct event *g_cmdev; static struct event *g_cacheev; +static struct commands_base *cmdbase; static int g_initialized; @@ -136,78 +122,6 @@ remove_tag(char *in, const char *tag) *(s - 1) = '\0'; } -/* ---------------------------- COMMAND EXECUTION -------------------------- */ - -static void -command_init(struct cache_command *cmd) -{ - memset(cmd, 0, sizeof(struct cache_command)); - - pthread_mutex_init(&cmd->lck, NULL); - pthread_cond_init(&cmd->cond, NULL); -} - -static void -command_deinit(struct cache_command *cmd) -{ - pthread_cond_destroy(&cmd->cond); - pthread_mutex_destroy(&cmd->lck); -} - -static int -send_command(struct cache_command *cmd) -{ - int ret; - - if (!cmd->func) - { - DPRINTF(E_LOG, L_CACHE, "BUG: cmd->func is NULL!\n"); - return -1; - } - - ret = write(g_cmd_pipe[1], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_CACHE, "Could not send command: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int -sync_command(struct cache_command *cmd) -{ - int ret; - - pthread_mutex_lock(&cmd->lck); - - ret = send_command(cmd); - if (ret < 0) - { - pthread_mutex_unlock(&cmd->lck); - return -1; - } - - pthread_cond_wait(&cmd->cond, &cmd->lck); - pthread_mutex_unlock(&cmd->lck); - - ret = cmd->ret; - - return ret; -} - -static int -nonblock_command(struct cache_command *cmd) -{ - int ret; - - ret = send_command(cmd); - if (ret < 0) - return -1; - - return 0; -} static void thread_exit(void) @@ -701,16 +615,18 @@ cache_daap_reply_add(const char *query, struct evbuffer *evbuf) } /* Adds the query to the list of queries for which we will build and cache a reply */ -static int -cache_daap_query_add(struct cache_command *cmd) +static enum command_state +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; char *query; char *errmsg; int ret; - if (!cmd->arg.ua) + cmdarg = arg; + if (!cmdarg->ua) { DPRINTF(E_LOG, L_CACHE, "Couldn't add slow query to cache, unknown user-agent\n"); @@ -718,16 +634,16 @@ cache_daap_query_add(struct cache_command *cmd) } // Currently we are only able to pre-build and cache these reply types - if ( (strncmp(cmd->arg.query, "/databases/1/containers/", strlen("/databases/1/containers/")) != 0) && - (strncmp(cmd->arg.query, "/databases/1/groups?", strlen("/databases/1/groups?")) != 0) && - (strncmp(cmd->arg.query, "/databases/1/items?", strlen("/databases/1/items?")) != 0) && - (strncmp(cmd->arg.query, "/databases/1/browse/", strlen("/databases/1/browse/")) != 0) ) + if ( (strncmp(cmdarg->query, "/databases/1/containers/", strlen("/databases/1/containers/")) != 0) && + (strncmp(cmdarg->query, "/databases/1/groups?", strlen("/databases/1/groups?")) != 0) && + (strncmp(cmdarg->query, "/databases/1/items?", strlen("/databases/1/items?")) != 0) && + (strncmp(cmdarg->query, "/databases/1/browse/", strlen("/databases/1/browse/")) != 0) ) goto error_add; - remove_tag(cmd->arg.query, "session-id"); - remove_tag(cmd->arg.query, "revision-number"); + remove_tag(cmdarg->query, "session-id"); + remove_tag(cmdarg->query, "revision-number"); - query = sqlite3_mprintf(Q_TMPL, cmd->arg.ua, cmd->arg.query, cmd->arg.msec, (int64_t)time(NULL)); + query = sqlite3_mprintf(Q_TMPL, cmdarg->ua, cmdarg->query, cmdarg->msec, (int64_t)time(NULL)); if (!query) { DPRINTF(E_LOG, L_CACHE, "Out of memory making query string.\n"); @@ -745,10 +661,10 @@ cache_daap_query_add(struct cache_command *cmd) goto error_add; } - DPRINTF(E_INFO, L_CACHE, "Slow query (%d ms) added to cache: '%s' (user-agent: '%s')\n", cmd->arg.msec, cmd->arg.query, cmd->arg.ua); + DPRINTF(E_INFO, L_CACHE, "Slow query (%d ms) added to cache: '%s' (user-agent: '%s')\n", cmdarg->msec, cmdarg->query, cmdarg->ua); - free(cmd->arg.ua); - free(cmd->arg.query); + free(cmdarg->ua); + free(cmdarg->query); // Limits the size of the cache to only contain replies for 20 most recent queries ret = sqlite3_exec(g_db_hdl, Q_CLEANUP, NULL, NULL, &errmsg); @@ -756,36 +672,41 @@ cache_daap_query_add(struct cache_command *cmd) { DPRINTF(E_LOG, L_CACHE, "Error cleaning up query list before update: %s\n", errmsg); sqlite3_free(errmsg); - return -1; + *retval = -1; + return COMMAND_END; } cache_daap_trigger(); - return 0; + *retval = 0; + return COMMAND_END; error_add: - if (cmd->arg.ua) - free(cmd->arg.ua); + if (cmdarg->ua) + free(cmdarg->ua); - if (cmd->arg.query) - free(cmd->arg.query); + if (cmdarg->query) + free(cmdarg->query); - return -1; + *retval = -1; + return COMMAND_END; #undef Q_CLEANUP #undef Q_TMPL } /* Gets a reply from the cache */ -static int -cache_daap_query_get(struct cache_command *cmd) +static enum command_state +cache_daap_query_get(void *arg, int *retval) { #define Q_TMPL "SELECT reply FROM replies WHERE query = ?;" + struct cache_arg *cmdarg; sqlite3_stmt *stmt; char *query; int datalen; int ret; - query = cmd->arg.query; + cmdarg = arg; + query = cmdarg->query; remove_tag(query, "session-id"); remove_tag(query, "revision-number"); @@ -795,7 +716,8 @@ cache_daap_query_get(struct cache_command *cmd) { DPRINTF(E_LOG, L_CACHE, "Error preparing query for cache update: %s\n", sqlite3_errmsg(g_db_hdl)); free(query); - return -1; + *retval = -1; + return COMMAND_END; } sqlite3_bind_text(stmt, 1, query, -1, SQLITE_STATIC); @@ -810,13 +732,13 @@ cache_daap_query_get(struct cache_command *cmd) datalen = sqlite3_column_bytes(stmt, 0); - if (!cmd->arg.evbuf) + if (!cmdarg->evbuf) { DPRINTF(E_LOG, L_CACHE, "Error: DAAP reply evbuffer is NULL\n"); goto error_get; } - ret = evbuffer_add(cmd->arg.evbuf, sqlite3_column_blob(stmt, 0), datalen); + ret = evbuffer_add(cmdarg->evbuf, sqlite3_column_blob(stmt, 0), datalen); if (ret < 0) { DPRINTF(E_LOG, L_CACHE, "Out of memory for DAAP reply evbuffer\n"); @@ -831,12 +753,14 @@ cache_daap_query_get(struct cache_command *cmd) free(query); - return 0; + *retval = 0; + return COMMAND_END; error_get: sqlite3_finalize(stmt); free(query); - return -1; + *retval = -1; + return COMMAND_END; #undef Q_TMPL } @@ -926,40 +850,55 @@ cache_daap_update_cb(int fd, short what, void *arg) * the actual cache update. The purpose is to avoid avoid cache updates when * the database is busy, eg during a library scan. */ -static int -cache_daap_update_timer(struct cache_command *cmd) +static enum command_state +cache_daap_update_timer(void *arg, int *ret) { if (!g_cacheev) - return -1; + { + *ret = -1; + return COMMAND_END; + } evtimer_add(g_cacheev, &g_wait); - return 0; + *ret = 0; + + return COMMAND_END; } -static int -cache_daap_suspend_timer(struct cache_command *cmd) +static enum command_state +cache_daap_suspend_timer(void *arg, int *ret) { if (!g_cacheev) - return -1; + { + *ret = -1; + return COMMAND_END; + } g_suspended = evtimer_pending(g_cacheev, NULL); if (g_suspended) evtimer_del(g_cacheev); - return 0; + *ret = 0; + + return COMMAND_END; } -static int -cache_daap_resume_timer(struct cache_command *cmd) +static enum command_state +cache_daap_resume_timer(void *arg, int *ret) { if (!g_cacheev) - return -1; + { + *ret = -1; + return COMMAND_END; + } if (g_suspended) evtimer_add(g_cacheev, &g_wait); - return 0; + *ret = 0; + + return COMMAND_END; } /* @@ -967,21 +906,23 @@ cache_daap_resume_timer(struct cache_command *cmd) * after the cached timestamp. All cache entries for the given path are deleted, if the file was * modified after the cached timestamp. * - * @param cmd->arg.path the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork) - * @param cmd->arg.mtime modified timestamp of the artwork file + * @param cmdarg->path the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork) + * @param cmdarg->mtime modified timestamp of the artwork file * @return 0 if successful, -1 if an error occurred */ -static int -cache_artwork_ping_impl(struct cache_command *cmd) +static enum command_state +cache_artwork_ping_impl(void *arg, int *retval) { #define Q_TMPL_PING "UPDATE artwork SET db_timestamp = %" PRIi64 " WHERE filepath = '%q' AND db_timestamp >= %" PRIi64 ";" #define Q_TMPL_DEL "DELETE FROM artwork WHERE filepath = '%q' AND db_timestamp < %" PRIi64 ";" + struct cache_arg *cmdarg; char *query; char *errmsg; int ret; - query = sqlite3_mprintf(Q_TMPL_PING, (int64_t)time(NULL), cmd->arg.path, (int64_t)cmd->arg.mtime); + cmdarg = arg; + query = sqlite3_mprintf(Q_TMPL_PING, (int64_t)time(NULL), cmdarg->path, (int64_t)cmdarg->mtime); DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query); @@ -994,9 +935,9 @@ cache_artwork_ping_impl(struct cache_command *cmd) goto error_ping; } - if (cmd->arg.del > 0) + if (cmdarg->del > 0) { - query = sqlite3_mprintf(Q_TMPL_DEL, cmd->arg.path, (int64_t)cmd->arg.mtime); + query = sqlite3_mprintf(Q_TMPL_DEL, cmdarg->path, (int64_t)cmdarg->mtime); DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query); @@ -1010,15 +951,17 @@ cache_artwork_ping_impl(struct cache_command *cmd) } } - free(cmd->arg.path); + free(cmdarg->path); - return 0; + *retval = 0; + return COMMAND_END; error_ping: sqlite3_free(errmsg); - free(cmd->arg.path); + free(cmdarg->path); - return -1; + *retval = -1; + return COMMAND_END; #undef Q_TMPL_PING #undef Q_TMPL_DEL @@ -1027,19 +970,21 @@ cache_artwork_ping_impl(struct cache_command *cmd) /* * Removes all cache entries for the given path * - * @param cmd->arg.path the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork) + * @param cmdarg->path the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork) * @return 0 if successful, -1 if an error occurred */ -static int -cache_artwork_delete_by_path_impl(struct cache_command *cmd) +static enum command_state +cache_artwork_delete_by_path_impl(void *arg, int *retval) { #define Q_TMPL_DEL "DELETE FROM artwork WHERE filepath = '%q';" + struct cache_arg *cmdarg; char *query; char *errmsg; int ret; - query = sqlite3_mprintf(Q_TMPL_DEL, cmd->arg.path); + cmdarg = arg; + query = sqlite3_mprintf(Q_TMPL_DEL, cmdarg->path); DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query); @@ -1050,12 +995,14 @@ cache_artwork_delete_by_path_impl(struct cache_command *cmd) DPRINTF(E_LOG, L_CACHE, "Query error: %s\n", errmsg); sqlite3_free(errmsg); - return -1; + *retval = -1; + return COMMAND_END; } DPRINTF(E_DBG, L_CACHE, "Deleted %d rows\n", sqlite3_changes(g_db_hdl)); - return 0; + *retval = 0; + return COMMAND_END; #undef Q_TMPL_DEL } @@ -1063,19 +1010,21 @@ cache_artwork_delete_by_path_impl(struct cache_command *cmd) /* * Removes all cache entries with cached timestamp older than the given reference timestamp * - * @param cmd->arg.mtime reference timestamp + * @param cmdarg->mtime reference timestamp * @return 0 if successful, -1 if an error occurred */ -static int -cache_artwork_purge_cruft_impl(struct cache_command *cmd) +static enum command_state +cache_artwork_purge_cruft_impl(void *arg, int *retval) { #define Q_TMPL "DELETE FROM artwork WHERE db_timestamp < %" PRIi64 ";" + struct cache_arg *cmdarg; char *query; char *errmsg; int ret; - query = sqlite3_mprintf(Q_TMPL, (int64_t)cmd->arg.mtime); + cmdarg = arg; + query = sqlite3_mprintf(Q_TMPL, (int64_t)cmdarg->mtime); DPRINTF(E_DBG, L_CACHE, "Running purge query '%s'\n", query); @@ -1086,12 +1035,14 @@ cache_artwork_purge_cruft_impl(struct cache_command *cmd) DPRINTF(E_LOG, L_CACHE, "Query error: %s\n", errmsg); sqlite3_free(errmsg); - return -1; + *retval = -1; + return COMMAND_END; } DPRINTF(E_DBG, L_CACHE, "Purged %d rows\n", sqlite3_changes(g_db_hdl)); - return 0; + *retval = 0; + return COMMAND_END; #undef Q_TMPL } @@ -1099,60 +1050,66 @@ cache_artwork_purge_cruft_impl(struct cache_command *cmd) /* * Adds the given (scaled) artwork image to the artwork cache * - * @param cmd->arg.persistentid persistent songalbumid or songartistid - * @param cmd->arg.max_w maximum image width - * @param cmd->arg.max_h maximum image height - * @param cmd->arg.format ART_FMT_PNG for png, ART_FMT_JPEG for jpeg or 0 if no artwork available - * @param cmd->arg.filename the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork) or empty if no artwork available - * @param cmd->arg.evbuf event buffer containing the (scaled) image + * @param cmdarg->persistentid persistent songalbumid or songartistid + * @param cmdarg->max_w maximum image width + * @param cmdarg->max_h maximum image height + * @param cmdarg->format ART_FMT_PNG for png, ART_FMT_JPEG for jpeg or 0 if no artwork available + * @param cmdarg->filename the full path to the artwork file (could be an jpg/png image or a media file with embedded artwork) or empty if no artwork available + * @param cmdarg->evbuf event buffer containing the (scaled) image * @return 0 if successful, -1 if an error occurred */ -static int -cache_artwork_add_impl(struct cache_command *cmd) +static enum command_state +cache_artwork_add_impl(void *arg, int *retval) { + struct cache_arg *cmdarg; sqlite3_stmt *stmt; char *query; uint8_t *data; int datalen; int ret; + cmdarg = arg; query = "INSERT INTO artwork (id, persistentid, max_w, max_h, format, filepath, db_timestamp, data, type) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?);"; ret = sqlite3_prepare_v2(g_db_hdl, query, -1, &stmt, 0); if (ret != SQLITE_OK) { DPRINTF(E_LOG, L_CACHE, "Could not prepare statement: %s\n", sqlite3_errmsg(g_db_hdl)); - return -1; + *retval = -1; + return COMMAND_END; } - datalen = evbuffer_get_length(cmd->arg.evbuf); - data = evbuffer_pullup(cmd->arg.evbuf, -1); + datalen = evbuffer_get_length(cmdarg->evbuf); + data = evbuffer_pullup(cmdarg->evbuf, -1); - sqlite3_bind_int64(stmt, 1, cmd->arg.persistentid); - sqlite3_bind_int(stmt, 2, cmd->arg.max_w); - sqlite3_bind_int(stmt, 3, cmd->arg.max_h); - sqlite3_bind_int(stmt, 4, cmd->arg.format); - sqlite3_bind_text(stmt, 5, cmd->arg.path, -1, SQLITE_STATIC); + sqlite3_bind_int64(stmt, 1, cmdarg->persistentid); + sqlite3_bind_int(stmt, 2, cmdarg->max_w); + sqlite3_bind_int(stmt, 3, cmdarg->max_h); + sqlite3_bind_int(stmt, 4, cmdarg->format); + sqlite3_bind_text(stmt, 5, cmdarg->path, -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 6, (uint64_t)time(NULL)); sqlite3_bind_blob(stmt, 7, data, datalen, SQLITE_STATIC); - sqlite3_bind_int(stmt, 8, cmd->arg.type); + sqlite3_bind_int(stmt, 8, cmdarg->type); ret = sqlite3_step(stmt); if (ret != SQLITE_DONE) { DPRINTF(E_LOG, L_CACHE, "Error stepping query for artwork add: %s\n", sqlite3_errmsg(g_db_hdl)); sqlite3_finalize(stmt); - return -1; + *retval = -1; + return COMMAND_END; } ret = sqlite3_finalize(stmt); if (ret != SQLITE_OK) { DPRINTF(E_LOG, L_CACHE, "Error finalizing query for artwork add: %s\n", sqlite3_errmsg(g_db_hdl)); - return -1; + *retval = -1; + return COMMAND_END; } - return 0; + *retval = 0; + return COMMAND_END; } /* @@ -1161,29 +1118,32 @@ cache_artwork_add_impl(struct cache_command *cmd) * If there is a cached entry for the given id and width/height, the parameter cached is set to 1. * In this case format and data contain the cached values. * - * @param cmd->arg.type individual or group artwork - * @param cmd->arg.persistentid persistent itemid, songalbumid or songartistid - * @param cmd->arg.max_w maximum image width - * @param cmd->arg.max_h maximum image height - * @param cmd->arg.cached set by this function to 0 if no cache entry exists, otherwise 1 - * @param cmd->arg.format set by this function to the format of the cache entry - * @param cmd->arg.evbuf event buffer filled by this function with the scaled image + * @param cmdarg->type individual or group artwork + * @param cmdarg->persistentid persistent itemid, songalbumid or songartistid + * @param cmdarg->max_w maximum image width + * @param cmdarg->max_h maximum image height + * @param cmdarg->cached set by this function to 0 if no cache entry exists, otherwise 1 + * @param cmdarg->format set by this function to the format of the cache entry + * @param cmdarg->evbuf event buffer filled by this function with the scaled image * @return 0 if successful, -1 if an error occurred */ -static int -cache_artwork_get_impl(struct cache_command *cmd) +static enum command_state +cache_artwork_get_impl(void *arg, int *retval) { #define Q_TMPL "SELECT a.format, a.data FROM artwork a WHERE a.type = %d AND a.persistentid = %" PRIi64 " AND a.max_w = %d AND a.max_h = %d;" + struct cache_arg *cmdarg; sqlite3_stmt *stmt; char *query; int datalen; int ret; - query = sqlite3_mprintf(Q_TMPL, cmd->arg.type, cmd->arg.persistentid, cmd->arg.max_w, cmd->arg.max_h); + cmdarg = arg; + query = sqlite3_mprintf(Q_TMPL, cmdarg->type, cmdarg->persistentid, cmdarg->max_w, cmdarg->max_h); if (!query) { DPRINTF(E_LOG, L_CACHE, "Out of memory for query string\n"); - return -1; + *retval = -1; + return COMMAND_END; } DPRINTF(E_DBG, L_CACHE, "Running query '%s'\n", query); @@ -1199,7 +1159,7 @@ cache_artwork_get_impl(struct cache_command *cmd) ret = sqlite3_step(stmt); if (ret != SQLITE_ROW) { - cmd->arg.cached = 0; + cmdarg->cached = 0; if (ret == SQLITE_DONE) { @@ -1215,16 +1175,16 @@ cache_artwork_get_impl(struct cache_command *cmd) goto error_get; } - cmd->arg.format = sqlite3_column_int(stmt, 0); + cmdarg->format = sqlite3_column_int(stmt, 0); datalen = sqlite3_column_bytes(stmt, 1); - if (!cmd->arg.evbuf) + if (!cmdarg->evbuf) { DPRINTF(E_LOG, L_CACHE, "Error: Artwork evbuffer is NULL\n"); ret = -1; goto error_get; } - ret = evbuffer_add(cmd->arg.evbuf, sqlite3_column_blob(stmt, 1), datalen); + ret = evbuffer_add(cmdarg->evbuf, sqlite3_column_blob(stmt, 1), datalen); if (ret < 0) { DPRINTF(E_LOG, L_CACHE, "Out of memory for artwork evbuffer\n"); @@ -1232,7 +1192,7 @@ cache_artwork_get_impl(struct cache_command *cmd) goto error_get; } - cmd->arg.cached = 1; + cmdarg->cached = 1; ret = sqlite3_finalize(stmt); if (ret != SQLITE_OK) @@ -1242,19 +1202,25 @@ cache_artwork_get_impl(struct cache_command *cmd) sqlite3_free(query); - return 0; + *retval = 0; + return COMMAND_END; error_get: sqlite3_finalize(stmt); sqlite3_free(query); - return ret; + *retval = ret; + return COMMAND_END; #undef Q_TMPL } -static int -cache_artwork_stash_impl(struct cache_command *cmd) +static enum command_state +cache_artwork_stash_impl(void *arg, int *retval) { + struct cache_arg *cmdarg; + + cmdarg = arg; + /* Clear current stash */ if (g_stash.path) { @@ -1263,40 +1229,50 @@ cache_artwork_stash_impl(struct cache_command *cmd) memset(&g_stash, 0, sizeof(struct stash)); } - g_stash.size = evbuffer_get_length(cmd->arg.evbuf); + g_stash.size = evbuffer_get_length(cmdarg->evbuf); g_stash.data = malloc(g_stash.size); if (!g_stash.data) { DPRINTF(E_LOG, L_CACHE, "Out of memory for artwork stash data\n"); - return -1; + *retval = -1; + return COMMAND_END; } - g_stash.path = strdup(cmd->arg.path); + g_stash.path = strdup(cmdarg->path); if (!g_stash.path) { DPRINTF(E_LOG, L_CACHE, "Out of memory for artwork stash path\n"); free(g_stash.data); - return -1; + *retval = -1; + return COMMAND_END; } - g_stash.format = cmd->arg.format; + g_stash.format = cmdarg->format; - return evbuffer_copyout(cmd->arg.evbuf, g_stash.data, g_stash.size); + *retval = evbuffer_copyout(cmdarg->evbuf, g_stash.data, g_stash.size); + return COMMAND_END; } -static int -cache_artwork_read_impl(struct cache_command *cmd) +static enum command_state +cache_artwork_read_impl(void *arg, int *retval) { - cmd->arg.format = 0; + struct cache_arg *cmdarg; - if (!g_stash.path || !g_stash.data || (strcmp(g_stash.path, cmd->arg.path) != 0)) - return -1; + cmdarg = arg; + cmdarg->format = 0; - cmd->arg.format = g_stash.format; + if (!g_stash.path || !g_stash.data || (strcmp(g_stash.path, cmdarg->path) != 0)) + { + *retval = -1; + return COMMAND_END; + } + + cmdarg->format = g_stash.format; DPRINTF(E_DBG, L_CACHE, "Stash hit (format %d, size %zu): %s\n", g_stash.format, g_stash.size, g_stash.path); - return evbuffer_add(cmd->arg.evbuf, g_stash.data, g_stash.size); + *retval = evbuffer_add(cmdarg->evbuf, g_stash.data, g_stash.size); + return COMMAND_END; } static void * @@ -1357,40 +1333,6 @@ exit_cb(int fd, short what, void *arg) event_add(g_exitev, NULL); } -static void -command_cb(int fd, short what, void *arg) -{ - struct cache_command *cmd; - int ret; - - ret = read(g_cmd_pipe[0], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_CACHE, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-"); - goto readd; - } - - if (cmd->nonblock) - { - cmd->func(cmd); - - free(cmd); - goto readd; - } - - pthread_mutex_lock(&cmd->lck); - - ret = cmd->func(cmd); - cmd->ret = ret; - - pthread_cond_signal(&cmd->cond); - pthread_mutex_unlock(&cmd->lck); - - readd: - event_add(g_cmdev, NULL); -} - - /* ---------------------------- DAAP cache API --------------------------- */ @@ -1404,122 +1346,66 @@ command_cb(int fd, short what, void *arg) void cache_daap_trigger(void) { - struct cache_command *cmd; - if (!g_initialized) return; - cmd = (struct cache_command *)malloc(sizeof(struct cache_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct cache_command)); - - cmd->nonblock = 1; - - cmd->func = cache_daap_update_timer; - - nonblock_command(cmd); + commands_exec_async(cmdbase, cache_daap_update_timer, NULL); } void cache_daap_suspend(void) { - struct cache_command *cmd; - if (!g_initialized) return; - cmd = (struct cache_command *)malloc(sizeof(struct cache_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct cache_command)); - - cmd->nonblock = 1; - - cmd->func = cache_daap_suspend_timer; - - nonblock_command(cmd); + commands_exec_async(cmdbase, cache_daap_suspend_timer, NULL); } void cache_daap_resume(void) { - struct cache_command *cmd; - if (!g_initialized) return; - cmd = (struct cache_command *)malloc(sizeof(struct cache_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct cache_command)); - - cmd->nonblock = 1; - - cmd->func = cache_daap_resume_timer; - - nonblock_command(cmd); + commands_exec_async(cmdbase, cache_daap_resume_timer, NULL); } int cache_daap_get(const char *query, struct evbuffer *evbuf) { - struct cache_command cmd; - int ret; + struct cache_arg cmdarg; if (!g_initialized) return -1; - command_init(&cmd); + cmdarg.query = strdup(query); + cmdarg.evbuf = evbuf; - cmd.func = cache_daap_query_get; - cmd.arg.query = strdup(query); - cmd.arg.evbuf = evbuf; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, cache_daap_query_get, NULL, &cmdarg); } void cache_daap_add(const char *query, const char *ua, int msec) { - struct cache_command *cmd; + struct cache_arg *cmdarg; if (!g_initialized) return; - cmd = (struct cache_command *)malloc(sizeof(struct cache_command)); - if (!cmd) + cmdarg = (struct cache_arg *)malloc(sizeof(struct cache_arg)); + if (!cmdarg) { - DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_command\n"); + DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_arg\n"); return; } - memset(cmd, 0, sizeof(struct cache_command)); + memset(cmdarg, 0, sizeof(struct cache_arg)); - cmd->nonblock = 1; + cmdarg->query = strdup(query); + cmdarg->ua = strdup(ua); + cmdarg->msec = msec; - cmd->func = cache_daap_query_add; - cmd->arg.query = strdup(query); - cmd->arg.ua = strdup(ua); - cmd->arg.msec = msec; - - nonblock_command(cmd); + commands_exec_async(cmdbase, cache_daap_query_add, cmdarg); } int @@ -1546,28 +1432,25 @@ cache_daap_threshold(void) void cache_artwork_ping(char *path, time_t mtime, int del) { - struct cache_command *cmd; + struct cache_arg *cmdarg; if (!g_initialized) return; - cmd = (struct cache_command *)malloc(sizeof(struct cache_command)); - if (!cmd) + cmdarg = (struct cache_arg *)malloc(sizeof(struct cache_arg)); + if (!cmdarg) { - DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_command\n"); + DPRINTF(E_LOG, L_CACHE, "Could not allocate cache_arg\n"); return; } - memset(cmd, 0, sizeof(struct cache_command)); + memset(cmdarg, 0, sizeof(struct cache_arg)); - cmd->nonblock = 1; + cmdarg->path = strdup(path); + cmdarg->mtime = mtime; + cmdarg->del = del; - cmd->func = cache_artwork_ping_impl; - cmd->arg.path = strdup(path); - cmd->arg.mtime = mtime; - cmd->arg.del = del; - - nonblock_command(cmd); + commands_exec_async(cmdbase, cache_artwork_ping_impl, cmdarg); } /* @@ -1579,22 +1462,14 @@ cache_artwork_ping(char *path, time_t mtime, int del) int cache_artwork_delete_by_path(char *path) { - struct cache_command cmd; - int ret; + struct cache_arg cmdarg; if (!g_initialized) return -1; - command_init(&cmd); + cmdarg.path = path; - cmd.func = cache_artwork_delete_by_path_impl; - cmd.arg.path = path; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, cache_artwork_delete_by_path_impl, NULL, &cmdarg); } /* @@ -1606,22 +1481,14 @@ cache_artwork_delete_by_path(char *path) int cache_artwork_purge_cruft(time_t ref) { - struct cache_command cmd; - int ret; + struct cache_arg cmdarg; if (!g_initialized) return -1; - command_init(&cmd); + cmdarg.mtime = ref; - cmd.func = cache_artwork_purge_cruft_impl; - cmd.arg.mtime = ref; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, cache_artwork_purge_cruft_impl, NULL, &cmdarg); } /* @@ -1639,28 +1506,20 @@ cache_artwork_purge_cruft(time_t ref) int cache_artwork_add(int type, int64_t persistentid, int max_w, int max_h, int format, char *filename, struct evbuffer *evbuf) { - struct cache_command cmd; - int ret; + struct cache_arg cmdarg; if (!g_initialized) return -1; - command_init(&cmd); + cmdarg.type = type; + cmdarg.persistentid = persistentid; + cmdarg.max_w = max_w; + cmdarg.max_h = max_h; + cmdarg.format = format; + cmdarg.path = filename; + cmdarg.evbuf = evbuf; - cmd.func = cache_artwork_add_impl; - cmd.arg.type = type; - cmd.arg.persistentid = persistentid; - cmd.arg.max_w = max_w; - cmd.arg.max_h = max_h; - cmd.arg.format = format; - cmd.arg.path = filename; - cmd.arg.evbuf = evbuf; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, cache_artwork_add_impl, NULL, &cmdarg); } /* @@ -1680,7 +1539,7 @@ cache_artwork_add(int type, int64_t persistentid, int max_w, int max_h, int form int cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cached, int *format, struct evbuffer *evbuf) { - struct cache_command cmd; + struct cache_arg cmdarg; int ret; if (!g_initialized) @@ -1690,21 +1549,16 @@ cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cac return 0; } - command_init(&cmd); + cmdarg.type = type; + cmdarg.persistentid = persistentid; + cmdarg.max_w = max_w; + cmdarg.max_h = max_h; + cmdarg.evbuf = evbuf; - cmd.func = cache_artwork_get_impl; - cmd.arg.type = type; - cmd.arg.persistentid = persistentid; - cmd.arg.max_w = max_w; - cmd.arg.max_h = max_h; - cmd.arg.evbuf = evbuf; + ret = commands_exec_sync(cmdbase, cache_artwork_get_impl, NULL, &cmdarg); - ret = sync_command(&cmd); - - *format = cmd.arg.format; - *cached = cmd.arg.cached; - - command_deinit(&cmd); + *format = cmdarg.format; + *cached = cmdarg.cached; return ret; } @@ -1720,24 +1574,16 @@ cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cac int cache_artwork_stash(struct evbuffer *evbuf, char *path, int format) { - struct cache_command cmd; - int ret; + struct cache_arg cmdarg; if (!g_initialized) return -1; - command_init(&cmd); + cmdarg.evbuf = evbuf; + cmdarg.path = path; + cmdarg.format = format; - cmd.func = cache_artwork_stash_impl; - cmd.arg.evbuf = evbuf; - cmd.arg.path = path; - cmd.arg.format = format; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, cache_artwork_stash_impl, NULL, &cmdarg); } /* @@ -1751,23 +1597,18 @@ cache_artwork_stash(struct evbuffer *evbuf, char *path, int format) int cache_artwork_read(struct evbuffer *evbuf, char *path, int *format) { - struct cache_command cmd; + struct cache_arg cmdarg; int ret; if (!g_initialized) return -1; - command_init(&cmd); + cmdarg.evbuf = evbuf; + cmdarg.path = path; - cmd.func = cache_artwork_read_impl; - cmd.arg.evbuf = evbuf; - cmd.arg.path = path; + ret = commands_exec_sync(cmdbase, cache_artwork_read_impl, NULL, &cmdarg); - ret = sync_command(&cmd); - - *format = cmd.arg.format; - - command_deinit(&cmd); + *format = cmdarg.format; return ret; } @@ -1807,17 +1648,6 @@ cache_init(void) goto exit_fail; } -#ifdef HAVE_PIPE2 - ret = pipe2(g_cmd_pipe, O_CLOEXEC); -#else - ret = pipe(g_cmd_pipe); -#endif - if (ret < 0) - { - DPRINTF(E_LOG, L_CACHE, "Could not create command pipe: %s\n", strerror(errno)); - goto cmd_fail; - } - evbase_cache = event_base_new(); if (!evbase_cache) { @@ -1832,22 +1662,16 @@ cache_init(void) goto evnew_fail; } - g_cmdev = event_new(evbase_cache, g_cmd_pipe[0], EV_READ, command_cb, NULL); - if (!g_cmdev) - { - DPRINTF(E_LOG, L_CACHE, "Could not create cmd event\n"); - goto evnew_fail; - } - g_cacheev = evtimer_new(evbase_cache, cache_daap_update_cb, NULL); - if (!g_cmdev) + if (!g_cacheev) { DPRINTF(E_LOG, L_CACHE, "Could not create cache event\n"); goto evnew_fail; } event_add(g_exitev, NULL); - event_add(g_cmdev, NULL); + + cmdbase = commands_base_new(evbase_cache); DPRINTF(E_INFO, L_CACHE, "cache thread init\n"); @@ -1868,15 +1692,12 @@ cache_init(void) return 0; thread_fail: + commands_base_free(cmdbase); evnew_fail: event_base_free(evbase_cache); evbase_cache = NULL; evbase_fail: - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); - - cmd_fail: close(g_exit_pipe[0]); close(g_exit_pipe[1]); @@ -1904,9 +1725,8 @@ cache_deinit(void) // Free event base (should free events too) event_base_free(evbase_cache); - // Close pipes - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); + // Close pipes and free command base + commands_base_free(cmdbase); close(g_exit_pipe[0]); close(g_exit_pipe[1]); } diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 00000000..e7ca47a1 --- /dev/null +++ b/src/commands.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2016 Christian Meffert + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "commands.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "logger.h" + + +struct command +{ + pthread_mutex_t lck; + pthread_cond_t cond; + + command_function func; + command_function func_bh; + void *arg; + int nonblock; + int ret; + int pending; +}; + +struct commands_base +{ + int command_pipe[2]; + struct event *command_event; + struct command *current_cmd; +}; + +/* + * Asynchronous execution of the command function + */ +static void +command_cb_async(struct commands_base *cmdbase, struct command *cmd) +{ + enum command_state cmdstate; + + // Command is executed asynchronously + cmdstate = cmd->func(cmd->arg, &cmd->ret); + + // Only free arg if there are no pending events (used in worker.c) + if (cmdstate == COMMAND_END && cmd->arg) + free(cmd->arg); + + free(cmd); + + event_add(cmdbase->command_event, NULL); +} + +/* + * Synchronous execution of the command function + */ +static void +command_cb_sync(struct commands_base *cmdbase, struct command *cmd) +{ + enum command_state cmdstate; + + pthread_mutex_lock(&cmd->lck); + + cmdstate = cmd->func(cmd->arg, &cmd->ret); + if (cmdstate == COMMAND_END) + { + // Command execution finished, execute the bottom half function + if (cmd->ret == 0 && cmd->func_bh) + { + cmdstate = cmd->func_bh(cmd->arg, &cmd->ret); + } + + // Signal the calling thread that the command execution finished + pthread_cond_signal(&cmd->cond); + pthread_mutex_unlock(&cmd->lck); + + event_add(cmdbase->command_event, NULL); + } + else + { + // Command execution is waiting for pending events before returning to the caller + cmdbase->current_cmd = cmd; + cmd->pending = cmd->ret; + } +} + +/* + * Event callback function + * + * Function is triggered by libevent if there is data to read on the command pipe (writing to the command pipe happens through + * the send_command function). + */ +static void +command_cb(int fd, short what, void *arg) +{ + struct commands_base *cmdbase; + struct command *cmd; + int ret; + + cmdbase = arg; + + // Get the command to execute from the pipe + ret = read(cmdbase->command_pipe[0], &cmd, sizeof(cmd)); + if (ret != sizeof(cmd)) + { + DPRINTF(E_LOG, L_MAIN, "Error reading command from command pipe: expected %zu bytes, read %d bytes\n", sizeof(cmd), ret); + + event_add(cmdbase->command_event, NULL); + return; + } + + // Execute the command function + if (cmd->nonblock) + { + // Command is executed asynchronously + command_cb_async(cmdbase, cmd); + } + else + { + // Command is executed synchronously, caller is waiting until signaled that the execution finished + command_cb_sync(cmdbase, cmd); + } +} + +/* + * Writes the given command to the command pipe + */ +static int +send_command(struct commands_base *cmdbase, struct command *cmd) +{ + int ret; + + if (!cmd->func) + { + DPRINTF(E_LOG, L_MAIN, "Programming error: send_command called with command->func NULL!\n"); + return -1; + } + + ret = write(cmdbase->command_pipe[1], &cmd, sizeof(cmd)); + if (ret != sizeof(cmd)) + { + return -1; + } + + return 0; +} + +/* + * Creates a new command base, needs to be freed by commands_base_free. + */ +struct commands_base * +commands_base_new(struct event_base *evbase) +{ + struct commands_base *cmdbase; + int ret; + + cmdbase = calloc(1, sizeof(struct commands_base)); + if (!cmdbase) + { + DPRINTF(E_LOG, L_MAIN, "Out of memory for cmdbase\n"); + return NULL; + } + +# if defined(__linux__) + ret = pipe2(cmdbase->command_pipe, O_CLOEXEC); +# else + ret = pipe(cmdbase->command_pipe); +# endif + if (ret < 0) + { + DPRINTF(E_LOG, L_MAIN, "Could not create command pipe: %s\n", strerror(errno)); + free(cmdbase); + return NULL; + } + + cmdbase->command_event = event_new(evbase, cmdbase->command_pipe[0], EV_READ, command_cb, cmdbase); + if (!cmdbase->command_event) + { + DPRINTF(E_LOG, L_MAIN, "Could not create cmd event\n"); + close(cmdbase->command_pipe[0]); + close(cmdbase->command_pipe[1]); + free(cmdbase); + return NULL; + } + + ret = event_add(cmdbase->command_event, NULL); + if (ret != 0) + { + DPRINTF(E_LOG, L_MAIN, "Could not add cmd event\n"); + close(cmdbase->command_pipe[0]); + close(cmdbase->command_pipe[1]); + free(cmdbase); + return NULL; + } + + return cmdbase; +} + +/* + * Frees the command base and closes the (internally used) pipes + */ +int +commands_base_free(struct commands_base *cmdbase) +{ + close(cmdbase->command_pipe[0]); + close(cmdbase->command_pipe[1]); + free(cmdbase); + + return 0; +} + +/* + * Gets the current return value for the current pending command. + * + * If a command has more than one pending event, each event can access the previous set return value + * if it depends on it. + * + * @param cmdbase The command base + * @return The current return value + */ +int +commands_exec_returnvalue(struct commands_base *cmdbase) +{ + if (cmdbase->current_cmd == NULL) + return 0; + + return cmdbase->current_cmd->ret; +} + +/* + * If a command function returned COMMAND_PENDING, each event triggered by this command needs to + * call command_exec_end, passing it the return value of the event execution. + * + * If a command function is waiting for multiple events, each event needs to call command_exec_end. + * The command base keeps track of the number of still pending events and only returns to the caller + * if there are no pending events left. + * + * @param cmdbase The command base (holds the current pending command) + * @param retvalue The return value for the calling thread + */ +void +commands_exec_end(struct commands_base *cmdbase, int retvalue) +{ + if (cmdbase->current_cmd == NULL) + return; + + // A pending event finished, decrease the number of pending events and update the return value + cmdbase->current_cmd->pending--; + cmdbase->current_cmd->ret = retvalue; + + DPRINTF(E_DBG, L_MAIN, "Command has %d pending events\n", cmdbase->current_cmd->pending); + + // If there are still pending events return + if (cmdbase->current_cmd->pending > 0) + return; + + // All pending events have finished, execute the bottom half and signal the caller that the command execution finished + if (cmdbase->current_cmd->func_bh) + { + cmdbase->current_cmd->func_bh(cmdbase->current_cmd->arg, &cmdbase->current_cmd->ret); + } + pthread_cond_signal(&cmdbase->current_cmd->cond); + pthread_mutex_unlock(&cmdbase->current_cmd->lck); + + cmdbase->current_cmd = NULL; + + /* Process commands again */ + event_add(cmdbase->command_event, NULL); +} + +/* + * Execute the function 'func' with the given argument 'arg' in the event loop thread. + * Blocks the caller (thread) until the function returned. + * + * If a function 'func_bh' ("bottom half") is given, it is executed after 'func' has successfully + * finished. + * + * @param cmdbase The command base + * @param func The function to be executed + * @param func_bh The bottom half function to be executed after all pending events from func are processed + * @param arg Argument passed to func (and func_bh) + * @return Return value of func (or func_bh if func_bh is not NULL) + */ +int +commands_exec_sync(struct commands_base *cmdbase, command_function func, command_function func_bh, void *arg) +{ + struct command cmd; + int ret; + + memset(&cmd, 0, sizeof(struct command)); + cmd.func = func; + cmd.func_bh = func_bh; + cmd.arg = arg; + cmd.nonblock = 0; + + pthread_mutex_lock(&cmd.lck); + + ret = send_command(cmdbase, &cmd); + if (ret < 0) + { + DPRINTF(E_LOG, L_MAIN, "Error sending command\n"); + pthread_mutex_unlock(&cmd.lck); + return -1; + } + + pthread_cond_wait(&cmd.cond, &cmd.lck); + pthread_mutex_unlock(&cmd.lck); + + return cmd.ret; +} + +/* + * Execute the function 'func' with the given argument 'arg' in the event loop thread. + * Triggers the function execution and immediately returns (does not wait for func to finish). + * + * The pointer passed as argument is freed in the event loop thread after func returned. + * + * @param cmdbase The command base + * @param func The function to be executed + * @param arg Argument passed to func + * @return 0 if triggering the function execution succeeded, -1 on failure. + */ +int +commands_exec_async(struct commands_base *cmdbase, command_function func, void *arg) +{ + struct command *cmd; + int ret; + + cmd = calloc(1, sizeof(struct command)); + cmd->func = func; + cmd->func_bh = NULL; + cmd->arg = arg; + cmd->nonblock = 1; + + ret = send_command(cmdbase, cmd); + if (ret < 0) + return -1; + + return 0; +} + diff --git a/src/commands.h b/src/commands.h new file mode 100644 index 00000000..5d4df0e1 --- /dev/null +++ b/src/commands.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Christian Meffert + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef SRC_COMMANDS_H_ +#define SRC_COMMANDS_H_ + +#include +#include + + +enum command_state { + COMMAND_END = 0, + COMMAND_PENDING = 1, +}; + +/* + * Function that will be executed in the event loop thread. + * + * If the function has pending events to complete, it needs to return COMMAND_PENDING with 'ret' set to + * the number of pending events to wait for. + * + * @param arg Opaque pointer passed by command_exec_sync or command_exec_async + * @param ret Pointer to the return value for the caller of the command + * @return COMMAND_END if there are no pending events (function execution is complete) or COMMAND_PENDING if there are pending events + */ +typedef enum command_state (*command_function)(void *arg, int *ret); + +struct commands_base; + + +struct commands_base * +commands_base_new(struct event_base *evbase); + +int +commands_base_free(struct commands_base *cmdbase); + +int +commands_exec_returnvalue(struct commands_base *cmdbase); + +void +commands_exec_end(struct commands_base *cmdbase, int retvalue); + +int +commands_exec_sync(struct commands_base *cmdbase, command_function func, command_function func_bh, void *arg); + +int +commands_exec_async(struct commands_base *cmdbase, command_function func, void *arg); + + +#endif /* SRC_COMMANDS_H_ */ diff --git a/src/filescanner.c b/src/filescanner.c index b6324dc3..2b19ea1f 100644 --- a/src/filescanner.c +++ b/src/filescanner.c @@ -61,6 +61,7 @@ #include "player.h" #include "cache.h" #include "artwork.h" +#include "commands.h" #ifdef LASTFM # include "lastfm.h" @@ -69,21 +70,6 @@ # include "spotify.h" #endif -struct filescanner_command; - -typedef int (*cmd_func)(struct filescanner_command *cmd); - -struct filescanner_command -{ - pthread_mutex_t lck; - pthread_cond_t cond; - - cmd_func func; - - int nonblock; - - int ret; -}; #define F_SCAN_BULK (1 << 0) #define F_SCAN_RESCAN (1 << 1) @@ -118,17 +104,16 @@ struct stacked_dir { struct stacked_dir *next; }; -static int cmd_pipe[2]; static int exit_pipe[2]; static int scan_exit; static int inofd; static struct event_base *evbase_scan; static struct event *inoev; static struct event *exitev; -static struct event *cmdev; static pthread_t tid_scan; static struct deferred_pl *playlists; static struct stacked_dir *dirstack; +static struct commands_base *cmdbase; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) struct deferred_file @@ -167,47 +152,12 @@ static int inofd_event_set(void); static void inofd_event_unset(void); -static int -filescanner_initscan(struct filescanner_command *cmd); -static int -filescanner_fullrescan(struct filescanner_command *cmd); +static enum command_state +filescanner_initscan(void *arg, int *retval); +static enum command_state +filescanner_fullrescan(void *arg, int *retval); -/* ---------------------------- COMMAND EXECUTION -------------------------- */ - -static int -send_command(struct filescanner_command *cmd) -{ - int ret; - - if (!cmd->func) - { - DPRINTF(E_LOG, L_SCAN, "BUG: cmd->func is NULL!\n"); - return -1; - } - - ret = write(cmd_pipe[1], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_SCAN, "Could not send command: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int -nonblock_command(struct filescanner_command *cmd) -{ - int ret; - - ret = send_command(cmd); - if (ret < 0) - return -1; - - return 0; -} - static int push_dir(struct stacked_dir **s, char *path, int parent_id) { @@ -855,6 +805,7 @@ static void process_file(char *file, time_t mtime, off_t size, int type, int flags, int dir_id) { int is_bulkscan; + int ret; is_bulkscan = (flags & F_SCAN_BULK); @@ -924,7 +875,7 @@ process_file(char *file, time_t mtime, off_t size, int type, int flags, int dir_ DPRINTF(E_LOG, L_SCAN, "Startup rescan triggered, found init-rescan file: %s\n", file); - filescanner_initscan(NULL); + filescanner_initscan(NULL, &ret); break; case FILE_CTRL_FULLSCAN: @@ -933,7 +884,7 @@ process_file(char *file, time_t mtime, off_t size, int type, int flags, int dir_ DPRINTF(E_LOG, L_SCAN, "Full rescan triggered, found full-rescan file: %s\n", file); - filescanner_fullrescan(NULL); + filescanner_fullrescan(NULL, &ret); break; default: @@ -1968,41 +1919,8 @@ exit_cb(int fd, short event, void *arg) scan_exit = 1; } -static void -command_cb(int fd, short what, void *arg) -{ - struct filescanner_command *cmd; - int ret; - - ret = read(cmd_pipe[0], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_SCAN, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-"); - goto readd; - } - - if (cmd->nonblock) - { - cmd->func(cmd); - - free(cmd); - goto readd; - } - - pthread_mutex_lock(&cmd->lck); - - ret = cmd->func(cmd); - cmd->ret = ret; - - pthread_cond_signal(&cmd->cond); - pthread_mutex_unlock(&cmd->lck); - - readd: - event_add(cmdev, NULL); -} - -static int -filescanner_initscan(struct filescanner_command *cmd) +static enum command_state +filescanner_initscan(void *arg, int *retval) { DPRINTF(E_LOG, L_SCAN, "Startup rescan triggered\n"); @@ -2012,11 +1930,12 @@ filescanner_initscan(struct filescanner_command *cmd) inofd_event_set(); bulk_scan(F_SCAN_BULK | F_SCAN_RESCAN); - return 0; + *retval = 0; + return COMMAND_END; } -static int -filescanner_fullrescan(struct filescanner_command *cmd) +static enum command_state +filescanner_fullrescan(void *arg, int *retval) { DPRINTF(E_LOG, L_SCAN, "Full rescan triggered\n"); @@ -2028,62 +1947,32 @@ filescanner_fullrescan(struct filescanner_command *cmd) inofd_event_set(); bulk_scan(F_SCAN_BULK); - return 0; + *retval = 0; + return COMMAND_END; } void filescanner_trigger_initscan(void) { - struct filescanner_command *cmd; - if (scanning) { DPRINTF(E_INFO, L_SCAN, "Scan already running, ignoring request to trigger a new init scan\n"); return; } - - cmd = (struct filescanner_command *)malloc(sizeof(struct filescanner_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_SCAN, "Could not allocate cache_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct filescanner_command)); - - cmd->nonblock = 1; - - cmd->func = filescanner_initscan; - - nonblock_command(cmd); + commands_exec_async(cmdbase, filescanner_initscan, NULL); } void filescanner_trigger_fullrescan(void) { - struct filescanner_command *cmd; - if (scanning) { DPRINTF(E_INFO, L_SCAN, "Scan already running, ignoring request to trigger a new init scan\n"); return; } - cmd = (struct filescanner_command *)malloc(sizeof(struct filescanner_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_SCAN, "Could not allocate cache_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct filescanner_command)); - - cmd->nonblock = 1; - - cmd->func = filescanner_fullrescan; - - nonblock_command(cmd); + commands_exec_async(cmdbase, filescanner_fullrescan, NULL); } /* @@ -2138,23 +2027,7 @@ filescanner_init(void) goto ino_fail; } -#ifdef HAVE_PIPE2 - ret = pipe2(cmd_pipe, O_CLOEXEC); -#else - ret = pipe(cmd_pipe); -#endif - if (ret < 0) - { - DPRINTF(E_LOG, L_SCAN, "Could not create command pipe: %s\n", strerror(errno)); - goto cmd_fail; - } - - cmdev = event_new(evbase_scan, cmd_pipe[0], EV_READ, command_cb, NULL); - if (!cmdev || (event_add(cmdev, NULL) < 0)) - { - DPRINTF(E_LOG, L_SCAN, "Could not create/add command event\n"); - goto cmd_fail; - } + cmdbase = commands_base_new(evbase_scan); ret = pthread_create(&tid_scan, NULL, filescanner, NULL); if (ret != 0) @@ -2173,9 +2046,7 @@ filescanner_init(void) return 0; thread_fail: - cmd_fail: - close(cmd_pipe[0]); - close(cmd_pipe[1]); + commands_base_free(cmdbase); close(inofd); exitev_fail: ino_fail: @@ -2214,9 +2085,8 @@ filescanner_deinit(void) inofd_event_unset(); + event_base_free(evbase_scan); + commands_base_free(cmdbase); close(exit_pipe[0]); close(exit_pipe[1]); - close(cmd_pipe[0]); - close(cmd_pipe[1]); - event_base_free(evbase_scan); } diff --git a/src/mpd.c b/src/mpd.c index cad44b1f..846280c2 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -62,6 +62,7 @@ #include "player.h" #include "queue.h" #include "filescanner.h" +#include "commands.h" static pthread_t tid_mpd; @@ -70,29 +71,12 @@ static struct event_base *evbase_mpd; static int g_exit_pipe[2]; static struct event *g_exitev; -static int g_cmd_pipe[2]; -static struct event *g_cmdev; +static struct commands_base *cmdbase; static struct evhttp *evhttpd; struct evconnlistener *listener; -struct mpd_command; - -typedef int (*cmd_func)(struct mpd_command *cmd); - -struct mpd_command -{ - pthread_mutex_t lck; - pthread_cond_t cond; - - cmd_func func; - - enum listener_event_type arg_evtype; - int nonblock; - - int ret; -}; #define COMMAND_ARGV_MAX 37 @@ -207,40 +191,6 @@ struct idle_client struct idle_client *idle_clients; -/* ---------------------------- COMMAND EXECUTION -------------------------- */ - -static int -send_command(struct mpd_command *cmd) -{ - int ret; - - if (!cmd->func) - { - DPRINTF(E_LOG, L_MPD, "BUG: cmd->func is NULL!\n"); - return -1; - } - - ret = write(g_cmd_pipe[1], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_MPD, "Could not send command: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int -nonblock_command(struct mpd_command *cmd) -{ - int ret; - - ret = send_command(cmd); - if (ret < 0) - return -1; - - return 0; -} static void thread_exit(void) @@ -3699,7 +3649,7 @@ mpd_command_decoders(struct evbuffer *evbuf, int argc, char **argv, char **errms return 0; } -struct command +struct mpd_command { /* The command name */ const char *mpdcommand; @@ -3716,7 +3666,7 @@ struct command int (*handler)(struct evbuffer *evbuf, int argc, char **argv, char **errmsg); }; -static struct command mpd_handlers[] = +static struct mpd_command mpd_handlers[] = { /* * Commands for querying status @@ -4192,7 +4142,7 @@ static struct command mpd_handlers[] = * @param name the name of the command * @return the command or NULL if it is an unknown/unsupported command */ -static struct command* +static struct mpd_command* mpd_find_command(const char *name) { int i; @@ -4240,7 +4190,7 @@ mpd_read_cb(struct bufferevent *bev, void *ctx) int ncmd; char *line; char *errmsg; - struct command *command; + struct mpd_command *command; enum command_list_type listtype; int idle_cmd; int close_cmd; @@ -4525,16 +4475,18 @@ mpd_notify_idle_client(struct idle_client *client, enum listener_event_type type return 0; } -static int -mpd_notify_idle(struct mpd_command *cmd) +static enum command_state +mpd_notify_idle(void *arg, int *retval) { + enum listener_event_type type; struct idle_client *client; struct idle_client *prev; struct idle_client *next; int i; int ret; - DPRINTF(E_DBG, L_MPD, "Notify clients waiting for idle results: %d\n", cmd->arg_evtype); + type = *(enum listener_event_type *)arg; + DPRINTF(E_DBG, L_MPD, "Notify clients waiting for idle results: %d\n", type); prev = NULL; next = NULL; @@ -4546,7 +4498,7 @@ mpd_notify_idle(struct mpd_command *cmd) next = client->next; - ret = mpd_notify_idle_client(client, cmd->arg_evtype); + ret = mpd_notify_idle_client(client, type); if (ret == 0) { @@ -4566,63 +4518,20 @@ mpd_notify_idle(struct mpd_command *cmd) i++; } - return 0; + *retval = 0; + return COMMAND_END; } static void mpd_listener_cb(enum listener_event_type type) { + enum listener_event_type *ptr; + + ptr = (enum listener_event_type *)malloc(sizeof(enum listener_event_type)); + *ptr = type; + DPRINTF(E_DBG, L_MPD, "Listener callback called with event type %d.\n", type); - struct mpd_command *cmd; - - cmd = (struct mpd_command *)malloc(sizeof(struct mpd_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_MPD, "Could not allocate cache_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct mpd_command)); - - cmd->nonblock = 1; - - cmd->func = mpd_notify_idle; - cmd->arg_evtype = type; - - nonblock_command(cmd); -} - -static void -command_cb(int fd, short what, void *arg) -{ - struct mpd_command *cmd; - int ret; - - ret = read(g_cmd_pipe[0], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_MPD, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-"); - goto readd; - } - - if (cmd->nonblock) - { - cmd->func(cmd); - - free(cmd); - goto readd; - } - - pthread_mutex_lock(&cmd->lck); - - ret = cmd->func(cmd); - cmd->ret = ret; - - pthread_cond_signal(&cmd->cond); - pthread_mutex_unlock(&cmd->lck); - - readd: - event_add(g_cmdev, NULL); + commands_exec_async(cmdbase, mpd_notify_idle, ptr); } /* @@ -4772,17 +4681,6 @@ int mpd_init(void) goto exit_fail; } -#ifdef HAVE_PIPE2 - ret = pipe2(g_cmd_pipe, O_CLOEXEC); -#else - ret = pipe(g_cmd_pipe); -#endif - if (ret < 0) - { - DPRINTF(E_LOG, L_MPD, "Could not create command pipe: %s\n", strerror(errno)); - goto cmd_fail; - } - evbase_mpd = event_base_new(); if (!evbase_mpd) { @@ -4799,15 +4697,7 @@ int mpd_init(void) event_add(g_exitev, NULL); - - g_cmdev = event_new(evbase_mpd, g_cmd_pipe[0], EV_READ, command_cb, NULL); - if (!g_cmdev) - { - DPRINTF(E_LOG, L_MPD, "Could not create cmd event\n"); - goto evnew_fail; - } - - event_add(g_cmdev, NULL); + cmdbase = commands_base_new(evbase_mpd); if (v6enabled) { @@ -4917,15 +4807,12 @@ int mpd_init(void) evhttp_fail: evconnlistener_free(listener); connew_fail: + commands_base_free(cmdbase); evnew_fail: event_base_free(evbase_mpd); evbase_mpd = NULL; evbase_fail: - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); - - cmd_fail: close(g_exit_pipe[0]); close(g_exit_pipe[1]); @@ -4975,9 +4862,8 @@ void mpd_deinit(void) // Free event base (should free events too) event_base_free(evbase_mpd); - // Close pipes + // Close pipes and free command base + commands_base_free(cmdbase); close(g_exit_pipe[0]); close(g_exit_pipe[1]); - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); } diff --git a/src/player.c b/src/player.c index fd1dbeb2..5b39576c 100644 --- a/src/player.c +++ b/src/player.c @@ -53,6 +53,7 @@ #include "player.h" #include "worker.h" #include "listener.h" +#include "commands.h" /* Audio outputs */ #include "outputs.h" @@ -135,9 +136,6 @@ struct volume_param { uint64_t spk_id; }; -struct player_command; -typedef int (*cmd_func)(struct player_command *cmd); - struct spk_enum { spk_enum_cb cb; @@ -198,50 +196,41 @@ struct player_metadata struct output_metadata *omd; }; -struct player_command +struct speaker_set_param { - pthread_mutex_t lck; - pthread_cond_t cond; + uint64_t *device_ids; + int intval; +}; - cmd_func func; - cmd_func func_bh; - - int nonblock; - - union { - struct volume_param vol_param; - void *noarg; - struct spk_enum *spk_enum; - struct output_device *device; - struct player_status *status; - struct player_source *ps; - struct player_metadata *pmd; - uint32_t *id_ptr; - uint64_t *device_ids; - enum repeat_mode mode; - uint32_t id; - int intval; - struct icy_artwork icy; - struct playback_start_param playback_start_param; - struct playerqueue_get_param queue_get_param; - struct playerqueue_add_param queue_add_param; - struct playerqueue_move_param queue_move_param; - struct playerqueue_remove_param queue_remove_param; - } arg; - - int ret; - - int output_requests_pending; +union player_arg +{ + struct volume_param vol_param; + void *noarg; + struct spk_enum *spk_enum; + struct output_device *device; + struct player_status *status; + struct player_source *ps; + struct player_metadata *pmd; + uint32_t *id_ptr; + struct speaker_set_param speaker_set_param; + enum repeat_mode mode; + uint32_t id; + int intval; + struct icy_artwork icy; + struct playback_start_param playback_start_param; + struct playerqueue_get_param queue_get_param; + struct playerqueue_add_param queue_add_param; + struct playerqueue_move_param queue_move_param; + struct playerqueue_remove_param queue_remove_param; }; struct event_base *evbase_player; static int exit_pipe[2]; -static int cmd_pipe[2]; static int player_exit; static struct event *exitev; -static struct event *cmdev; static pthread_t tid_player; +static struct commands_base *cmdbase; /* Config values */ static int clear_queue_on_stop_disabled; @@ -286,9 +275,6 @@ static struct output_device *dev_list; /* Output status */ static int output_sessions; -/* Commands */ -static struct player_command *cur_cmd; - /* Last commanded volume */ static int master_volume; @@ -308,35 +294,6 @@ static struct queue *queue; /* Play history */ static struct player_history *history; -/* Command helpers */ -static void -command_async_end(struct player_command *cmd) -{ - cur_cmd = NULL; - - pthread_cond_signal(&cmd->cond); - pthread_mutex_unlock(&cmd->lck); - - /* Process commands again */ - event_add(cmdev, NULL); -} - -static void -command_init(struct player_command *cmd) -{ - memset(cmd, 0, sizeof(struct player_command)); - - pthread_mutex_init(&cmd->lck, NULL); - pthread_cond_init(&cmd->cond, NULL); -} - -static void -command_deinit(struct player_command *cmd) -{ - pthread_cond_destroy(&cmd->cond); - pthread_mutex_destroy(&cmd->lck); -} - static void status_update(enum play_status status) @@ -543,8 +500,8 @@ pb_timer_stop(void) static void playback_abort(void); -static int -playerqueue_clear(struct player_command *cmd); +static enum command_state +playerqueue_clear(void *arg, int *retval); static void player_metadata_send(struct player_metadata *pmd); @@ -1672,15 +1629,17 @@ device_check(struct output_device *check) return (device) ? 0 : -1; } -static int -device_add(struct player_command *cmd) +static enum command_state +device_add(void *arg, int *retval) { + union player_arg *cmdarg; struct output_device *add; struct output_device *device; int selected; int ret; - add = cmd->arg.device; + cmdarg = arg; + add = cmdarg->device; for (device = dev_list; device; device = device->next) { @@ -1748,16 +1707,19 @@ device_add(struct player_command *cmd) device_list_sort(); - return 0; + *retval = 0; + return COMMAND_END; } -static int -device_remove_family(struct player_command *cmd) +static enum command_state +device_remove_family(void *arg, int *retval) { + union player_arg *cmdarg; struct output_device *remove; struct output_device *device; - remove = cmd->arg.device; + cmdarg = arg; + remove = cmdarg->device; for (device = dev_list; device; device = device->next) { @@ -1770,7 +1732,8 @@ device_remove_family(struct player_command *cmd) DPRINTF(E_WARN, L_PLAYER, "The %s device '%s' stopped advertising, but not in our list\n", remove->type_name, remove->name); outputs_device_free(remove); - return 0; + *retval = 0; + return COMMAND_END; } /* v{4,6}_port non-zero indicates the address family stopped advertising */ @@ -1798,15 +1761,18 @@ device_remove_family(struct player_command *cmd) outputs_device_free(remove); - return 0; + *retval = 0; + return COMMAND_END; } -static int -metadata_send(struct player_command *cmd) +static enum command_state +metadata_send(void *arg, int *retval) { + union player_arg *cmdarg; struct player_metadata *pmd; - pmd = cmd->arg.pmd; + cmdarg = arg; + pmd = cmdarg->pmd; /* Do the setting of rtptime which was deferred in metadata_trigger because we * wanted to wait until we had the actual last_rtptime @@ -1816,7 +1782,8 @@ metadata_send(struct player_command *cmd) outputs_metadata_send(pmd->omd, pmd->rtptime, pmd->offset, pmd->startup); - return 0; + *retval = 0; + return COMMAND_END; } /* Output device callbacks executed in the player thread */ @@ -1873,43 +1840,33 @@ device_command_cb(struct output_device *device, struct output_session *session, { DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_command_cb\n", outputs_name(device->type)); - cur_cmd->output_requests_pending--; - outputs_status_cb(session, device_streaming_cb); if (status == OUTPUT_STATE_FAILED) device_streaming_cb(device, session, status); - if (cur_cmd->output_requests_pending == 0) - { - if (cur_cmd->func_bh) - cur_cmd->ret = cur_cmd->func_bh(cur_cmd); - else - cur_cmd->ret = 0; - - command_async_end(cur_cmd); - } + commands_exec_end(cmdbase, 0); } static void device_shutdown_cb(struct output_device *device, struct output_session *session, enum output_device_state status) { + int retval; int ret; DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_shutdown_cb\n", outputs_name(device->type)); - cur_cmd->output_requests_pending--; - if (output_sessions) output_sessions--; + retval = commands_exec_returnvalue(cmdbase); ret = device_check(device); if (ret < 0) { DPRINTF(E_WARN, L_PLAYER, "Output device disappeared before shutdown completion!\n"); - if (cur_cmd->ret != -2) - cur_cmd->ret = -1; + if (retval != -2) + retval = -1; goto out; } @@ -1919,14 +1876,11 @@ device_shutdown_cb(struct output_device *device, struct output_session *session, device_remove(device); out: - if (cur_cmd->output_requests_pending == 0) - { - /* cur_cmd->ret already set - * - to 0 (or -2 if password issue) in speaker_set() - * - to -1 above on error - */ - command_async_end(cur_cmd); - } + /* cur_cmd->ret already set + * - to 0 (or -2 if password issue) in speaker_set() + * - to -1 above on error + */ + commands_exec_end(cmdbase, retval); } static void @@ -1945,12 +1899,12 @@ static void device_activate_cb(struct output_device *device, struct output_session *session, enum output_device_state status) { struct timespec ts; + int retval; int ret; DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_activate_cb\n", outputs_name(device->type)); - cur_cmd->output_requests_pending--; - + retval = commands_exec_returnvalue(cmdbase); ret = device_check(device); if (ret < 0) { @@ -1959,15 +1913,15 @@ device_activate_cb(struct output_device *device, struct output_session *session, outputs_status_cb(session, device_lost_cb); outputs_device_stop(session); - if (cur_cmd->ret != -2) - cur_cmd->ret = -1; + if (retval != -2) + retval = -1; goto out; } if (status == OUTPUT_STATE_PASSWORD) { status = OUTPUT_STATE_FAILED; - cur_cmd->ret = -2; + retval = -2; } if (status == OUTPUT_STATE_FAILED) @@ -1977,8 +1931,8 @@ device_activate_cb(struct output_device *device, struct output_session *session, if (!device->advertised) device_remove(device); - if (cur_cmd->ret != -2) - cur_cmd->ret = -1; + if (retval != -2) + retval = -1; goto out; } @@ -2004,40 +1958,37 @@ device_activate_cb(struct output_device *device, struct output_session *session, outputs_status_cb(session, device_streaming_cb); out: - if (cur_cmd->output_requests_pending == 0) - { - /* cur_cmd->ret already set - * - to 0 in speaker_set() (default) - * - to -2 above if password issue - * - to -1 above on error - */ - command_async_end(cur_cmd); - } + /* cur_cmd->ret already set + * - to 0 in speaker_set() (default) + * - to -2 above if password issue + * - to -1 above on error + */ + commands_exec_end(cmdbase, retval); } static void device_probe_cb(struct output_device *device, struct output_session *session, enum output_device_state status) { + int retval; int ret; DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_probe_cb\n", outputs_name(device->type)); - cur_cmd->output_requests_pending--; - + retval = commands_exec_returnvalue(cmdbase); ret = device_check(device); if (ret < 0) { DPRINTF(E_WARN, L_PLAYER, "Output device disappeared during probe!\n"); - if (cur_cmd->ret != -2) - cur_cmd->ret = -1; + if (retval != -2) + retval = -1; goto out; } if (status == OUTPUT_STATE_PASSWORD) { status = OUTPUT_STATE_FAILED; - cur_cmd->ret = -2; + retval = -2; } if (status == OUTPUT_STATE_FAILED) @@ -2047,21 +1998,18 @@ device_probe_cb(struct output_device *device, struct output_session *session, en if (!device->advertised) device_remove(device); - if (cur_cmd->ret != -2) - cur_cmd->ret = -1; + if (retval != -2) + retval = -1; goto out; } out: - if (cur_cmd->output_requests_pending == 0) - { - /* cur_cmd->ret already set - * - to 0 in speaker_set() (default) - * - to -2 above if password issue - * - to -1 above on error - */ - command_async_end(cur_cmd); - } + /* cur_cmd->ret already set + * - to 0 in speaker_set() (default) + * - to -2 above if password issue + * - to -1 above on error + */ + commands_exec_end(cmdbase, retval); } static void @@ -2071,8 +2019,6 @@ device_restart_cb(struct output_device *device, struct output_session *session, DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_restart_cb\n", outputs_name(device->type)); - cur_cmd->output_requests_pending--; - ret = device_check(device); if (ret < 0) { @@ -2100,18 +2046,15 @@ device_restart_cb(struct output_device *device, struct output_session *session, outputs_status_cb(session, device_streaming_cb); out: - if (cur_cmd->output_requests_pending == 0) - { - cur_cmd->ret = cur_cmd->func_bh(cur_cmd); - - command_async_end(cur_cmd); - } + commands_exec_end(cmdbase, 0); } /* Internal abort routine */ static void playback_abort(void) { + int ret; + outputs_playback_stop(); pb_timer_stop(); @@ -2121,7 +2064,7 @@ playback_abort(void) evbuffer_drain(audio_buf, evbuffer_get_length(audio_buf)); if (!clear_queue_on_stop_disabled) - playerqueue_clear(NULL); + playerqueue_clear(NULL, &ret); status_update(PLAY_STOPPED); @@ -2129,9 +2072,10 @@ playback_abort(void) } /* Actual commands, executed in the player thread */ -static int -get_status(struct player_command *cmd) +static enum command_state +get_status(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct timespec ts; struct player_source *ps; struct player_status *status; @@ -2139,7 +2083,7 @@ get_status(struct player_command *cmd) uint64_t pos; int ret; - status = cmd->arg.status; + status = cmdarg->status; memset(status, 0, sizeof(struct player_status)); @@ -2231,52 +2175,66 @@ get_status(struct player_command *cmd) break; } - return 0; + *retval = 0; + return COMMAND_END; } -static int -now_playing(struct player_command *cmd) +static enum command_state +now_playing(void *arg, int *retval) { + union player_arg *cmdarg = arg; uint32_t *id; struct player_source *ps_playing; - id = cmd->arg.id_ptr; + id = cmdarg->id_ptr; ps_playing = source_now_playing(); if (ps_playing) *id = ps_playing->id; else - return -1; + { + *retval = -1; + return COMMAND_END; + } - return 0; + *retval = 0; + return COMMAND_END; } -static int -artwork_url_get(struct player_command *cmd) +static enum command_state +artwork_url_get(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct player_source *ps; - cmd->arg.icy.artwork_url = NULL; + cmdarg->icy.artwork_url = NULL; if (cur_playing) ps = cur_playing; else if (cur_streaming) ps = cur_streaming; else - return -1; + { + *retval = -1; + return COMMAND_END; + } /* Check that we are playing a viable stream, and that it has the requested id */ - if (!ps->xcode || ps->data_kind != DATA_KIND_HTTP || ps->id != cmd->arg.icy.id) - return -1; + if (!ps->xcode || ps->data_kind != DATA_KIND_HTTP || ps->id != cmdarg->icy.id) + { + *retval = -1; + return COMMAND_END; + } - cmd->arg.icy.artwork_url = transcode_metadata_artwork_url(ps->xcode); + cmdarg->icy.artwork_url = transcode_metadata_artwork_url(ps->xcode); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_stop(struct player_command *cmd) +static enum command_state +playback_stop(void *arg, int *retval) { struct player_source *ps_playing; @@ -2284,7 +2242,7 @@ playback_stop(struct player_command *cmd) * full stop just yet; this saves time when restarting, which is nicer * for the user. */ - cmd->output_requests_pending = outputs_flush(device_command_cb, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES); + *retval = outputs_flush(device_command_cb, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES); pb_timer_stop(); @@ -2303,15 +2261,15 @@ playback_stop(struct player_command *cmd) metadata_purge(); /* We're async if we need to flush devices */ - if (cmd->output_requests_pending > 0) - return 1; /* async */ + if (*retval > 0) + return COMMAND_PENDING; /* async */ - return 0; + return COMMAND_END; } /* Playback startup bottom half */ -static int -playback_start_bh(struct player_command *cmd) +static enum command_state +playback_start_bh(void *arg, int *retval) { int ret; @@ -2351,16 +2309,18 @@ playback_start_bh(struct player_command *cmd) status_update(PLAY_PLAYING); - return 0; + *retval = 0; + return COMMAND_END; out_fail: playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } -static int -playback_start_item(struct player_command *cmd, struct queue_item *qii) +static enum command_state +playback_start_item(union player_arg *cmdarg, int *retval, struct queue_item *qii) { uint32_t *dbmfi_id; struct output_device *device; @@ -2368,7 +2328,7 @@ playback_start_item(struct player_command *cmd, struct queue_item *qii) struct queue_item *item; int ret; - dbmfi_id = cmd->arg.playback_start_param.id_ptr; + dbmfi_id = cmdarg->playback_start_param.id_ptr; ps_playing = source_now_playing(); @@ -2386,7 +2346,8 @@ playback_start_item(struct player_command *cmd, struct queue_item *qii) status_update(player_state); - return 0; + *retval = 0; + return COMMAND_END; } // Update global playback position @@ -2419,7 +2380,8 @@ playback_start_item(struct player_command *cmd, struct queue_item *qii) if (ret < 0) { playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } @@ -2429,7 +2391,7 @@ playback_start_item(struct player_command *cmd, struct queue_item *qii) metadata_trigger(1); /* Start sessions on selected devices */ - cmd->output_requests_pending = 0; + *retval = 0; for (device = dev_list; device; device = device->next) { @@ -2443,12 +2405,12 @@ playback_start_item(struct player_command *cmd, struct queue_item *qii) } DPRINTF(E_INFO, L_PLAYER, "Using selected %s device '%s'\n", device->type_name, device->name); - cmd->output_requests_pending++; + (*retval)++; } } /* Try to autoselect a non-selected device if the above failed */ - if ((cmd->output_requests_pending == 0) && (output_sessions == 0)) + if ((*retval == 0) && (output_sessions == 0)) for (device = dev_list; device; device = device->next) { if ((outputs_priority(device) == 0) || device->session) @@ -2464,67 +2426,72 @@ playback_start_item(struct player_command *cmd, struct queue_item *qii) } DPRINTF(E_INFO, L_PLAYER, "Autoselecting %s device '%s'\n", device->type_name, device->name); - cmd->output_requests_pending++; + (*retval)++; break; } /* No luck finding valid output */ - if ((cmd->output_requests_pending == 0) && (output_sessions == 0)) + if ((*retval == 0) && (output_sessions == 0)) { DPRINTF(E_LOG, L_PLAYER, "Could not start playback: no output selected or couldn't start any output\n"); playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } /* We're async if we need to start devices */ - if (cmd->output_requests_pending > 0) - return 1; /* async */ + if (*retval > 0) + return COMMAND_PENDING; /* async */ /* Otherwise, just run the bottom half */ - return playback_start_bh(cmd); + *retval = 0; + return COMMAND_END; } -static int -playback_start(struct player_command *cmd) +static enum command_state +playback_start(void *arg, int *retval) { - return playback_start_item(cmd, NULL); + return playback_start_item(arg, retval, NULL); } -static int -playback_start_byitemid(struct player_command *cmd) +static enum command_state +playback_start_byitemid(void *arg, int *retval) { + union player_arg *cmdarg = arg; int item_id; struct queue_item *qii; - item_id = cmd->arg.playback_start_param.id; + item_id = cmdarg->playback_start_param.id; qii = queue_get_byitemid(queue, item_id); - return playback_start_item(cmd, qii); + return playback_start_item(cmdarg, retval, qii); } -static int -playback_start_byindex(struct player_command *cmd) +static enum command_state +playback_start_byindex(void *arg, int *retval) { + union player_arg *cmdarg = arg; int pos; struct queue_item *qii; - pos = cmd->arg.playback_start_param.pos; + pos = cmdarg->playback_start_param.pos; qii = queue_get_byindex(queue, pos, 0); - return playback_start_item(cmd, qii); + return playback_start_item(cmdarg, retval, qii); } -static int -playback_start_bypos(struct player_command *cmd) +static enum command_state +playback_start_bypos(void *arg, int *retval) { + union player_arg *cmdarg = arg; int offset; struct player_source *ps_playing; struct queue_item *qii; - offset = cmd->arg.playback_start_param.pos; + offset = cmdarg->playback_start_param.pos; ps_playing = source_now_playing(); @@ -2537,11 +2504,11 @@ playback_start_bypos(struct player_command *cmd) qii = queue_get_byindex(queue, offset, shuffle); } - return playback_start_item(cmd, qii); + return playback_start_item(cmdarg, retval, qii); } -static int -playback_prev_bh(struct player_command *cmd) +static enum command_state +playback_prev_bh(void *arg, int *retval) { int ret; int pos_sec; @@ -2554,7 +2521,8 @@ playback_prev_bh(struct player_command *cmd) if (!cur_streaming) { DPRINTF(E_LOG, L_PLAYER, "Could not get current stream source\n"); - return -1; + *retval = -1; + return COMMAND_END; } /* Only add to history if playback started. */ @@ -2576,7 +2544,8 @@ playback_prev_bh(struct player_command *cmd) if (!item) { playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } source_stop(); @@ -2596,24 +2565,29 @@ playback_prev_bh(struct player_command *cmd) { playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } } if (player_state == PLAY_STOPPED) - return -1; + { + *retval = -1; + return COMMAND_END; + } /* Silent status change - playback_start() sends the real status update */ player_state = PLAY_PAUSED; - return 0; + *retval = 0; + return COMMAND_END; } /* * The bottom half of the next command */ -static int -playback_next_bh(struct player_command *cmd) +static enum command_state +playback_next_bh(void *arg, int *retval) { int ret; struct queue_item *item; @@ -2625,7 +2599,8 @@ playback_next_bh(struct player_command *cmd) if (!cur_streaming) { DPRINTF(E_LOG, L_PLAYER, "Could not get current stream source\n"); - return -1; + *retval = -1; + return COMMAND_END; } /* Only add to history if playback started. */ @@ -2636,7 +2611,8 @@ playback_next_bh(struct player_command *cmd) if (!item) { playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } source_stop(); @@ -2645,25 +2621,31 @@ playback_next_bh(struct player_command *cmd) if (ret < 0) { playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } if (player_state == PLAY_STOPPED) - return -1; + { + *retval = -1; + return COMMAND_END; + } /* Silent status change - playback_start() sends the real status update */ player_state = PLAY_PAUSED; - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_seek_bh(struct player_command *cmd) +static enum command_state +playback_seek_bh(void *arg, int *retval) { + union player_arg *cmdarg = arg; int ms; int ret; - ms = cmd->arg.intval; + ms = cmdarg->intval; ret = source_seek(ms); @@ -2671,17 +2653,19 @@ playback_seek_bh(struct player_command *cmd) { playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } /* Silent status change - playback_start() sends the real status update */ player_state = PLAY_PAUSED; - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_pause_bh(struct player_command *cmd) +static enum command_state +playback_pause_bh(void *arg, int *retval) { int ret; @@ -2691,7 +2675,8 @@ playback_pause_bh(struct player_command *cmd) DPRINTF(E_DBG, L_PLAYER, "Source is not pausable, abort playback\n"); playback_abort(); - return -1; + *retval = -1; + return COMMAND_END; } status_update(PLAY_PAUSED); @@ -2701,11 +2686,12 @@ playback_pause_bh(struct player_command *cmd) db_file_save_seek(cur_streaming->id, ret); } - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_pause(struct player_command *cmd) +static enum command_state +playback_pause(void *arg, int *retval) { uint64_t pos; @@ -2720,9 +2706,12 @@ playback_pause(struct player_command *cmd) /* Make sure playback is still running after source_check() */ if (player_state == PLAY_STOPPED) - return -1; + { + *retval = -1; + return COMMAND_END; + } - cmd->output_requests_pending = outputs_flush(device_command_cb, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES); + *retval = outputs_flush(device_command_cb, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES); pb_timer_stop(); @@ -2733,21 +2722,22 @@ playback_pause(struct player_command *cmd) metadata_purge(); /* We're async if we need to flush devices */ - if (cmd->output_requests_pending > 0) - return 1; /* async */ + if (*retval > 0) + return COMMAND_PENDING; /* async */ /* Otherwise, just run the bottom half */ - return cmd->func_bh(cmd); + return COMMAND_END; } -static int -speaker_enumerate(struct player_command *cmd) +static enum command_state +speaker_enumerate(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct output_device *device; struct spk_enum *spk_enum; struct spk_flags flags; - spk_enum = cmd->arg.spk_enum; + spk_enum = cmdarg->spk_enum; #ifdef DEBUG_RELVOL DPRINTF(E_DBG, L_PLAYER, "*** master: %d\n", master_volume); @@ -2769,7 +2759,8 @@ speaker_enumerate(struct player_command *cmd) } } - return 0; + *retval = 0; + return COMMAND_END; } static int @@ -2820,16 +2811,18 @@ speaker_deactivate(struct output_device *device) return 0; } -static int -speaker_set(struct player_command *cmd) +static enum command_state +speaker_set(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct output_device *device; uint64_t *ids; int nspk; int i; int ret; - ids = cmd->arg.device_ids; + *retval = 0; + ids = cmdarg->speaker_set_param.device_ids; if (ids) nspk = ids[0]; @@ -2838,8 +2831,7 @@ speaker_set(struct player_command *cmd) DPRINTF(E_DBG, L_PLAYER, "Speaker set: %d speakers\n", nspk); - cmd->output_requests_pending = 0; - cmd->ret = 0; + *retval = 0; for (device = dev_list; device; device = device->next) { @@ -2857,7 +2849,7 @@ speaker_set(struct player_command *cmd) { DPRINTF(E_INFO, L_PLAYER, "The %s device '%s' is password-protected, but we don't have it\n", device->type_name, device->name); - cmd->ret = -2; + cmdarg->speaker_set_param.intval = -2; continue; } @@ -2875,11 +2867,11 @@ speaker_set(struct player_command *cmd) speaker_deselect_output(device); - if (cmd->ret != -2) - cmd->ret = -1; + if (cmdarg->speaker_set_param.intval != -2) + cmdarg->speaker_set_param.intval = -1; } else - cmd->output_requests_pending++; + (*retval)++; } } else @@ -2896,38 +2888,39 @@ speaker_set(struct player_command *cmd) { DPRINTF(E_LOG, L_PLAYER, "Could not deactivate %s device '%s'\n", device->type_name, device->name); - if (cmd->ret != -2) - cmd->ret = -1; + if (cmdarg->speaker_set_param.intval != -2) + cmdarg->speaker_set_param.intval = -1; } else - cmd->output_requests_pending++; + (*retval)++; } } } listener_notify(LISTENER_SPEAKER); - if (cmd->output_requests_pending > 0) - return 1; /* async */ + if (*retval > 0) + return COMMAND_PENDING; /* async */ - return cmd->ret; + *retval = cmdarg->speaker_set_param.intval; + return COMMAND_END; } -static int -volume_set(struct player_command *cmd) +static enum command_state +volume_set(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct output_device *device; int volume; - volume = cmd->arg.intval; + *retval = 0; + volume = cmdarg->intval; if (master_volume == volume) - return 0; + return COMMAND_END; master_volume = volume; - cmd->output_requests_pending = 0; - for (device = dev_list; device; device = device->next) { if (!device->selected) @@ -2940,26 +2933,28 @@ volume_set(struct player_command *cmd) #endif if (device->session) - cmd->output_requests_pending += outputs_device_volume_set(device, device_command_cb); + *retval += outputs_device_volume_set(device, device_command_cb); } listener_notify(LISTENER_VOLUME); - if (cmd->output_requests_pending > 0) - return 1; /* async */ + if (*retval > 0) + return COMMAND_PENDING; /* async */ - return 0; + return COMMAND_END; } -static int -volume_setrel_speaker(struct player_command *cmd) +static enum command_state +volume_setrel_speaker(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct output_device *device; uint64_t id; int relvol; - id = cmd->arg.vol_param.spk_id; - relvol = cmd->arg.vol_param.volume; + *retval = 0; + id = cmdarg->vol_param.spk_id; + relvol = cmdarg->vol_param.volume; for (device = dev_list; device; device = device->next) { @@ -2977,28 +2972,30 @@ volume_setrel_speaker(struct player_command *cmd) #endif if (device->session) - cmd->output_requests_pending = outputs_device_volume_set(device, device_command_cb); + *retval = outputs_device_volume_set(device, device_command_cb); break; } listener_notify(LISTENER_VOLUME); - if (cmd->output_requests_pending > 0) - return 1; /* async */ + if (*retval > 0) + return COMMAND_PENDING; /* async */ - return 0; + return COMMAND_END; } -static int -volume_setabs_speaker(struct player_command *cmd) +static enum command_state +volume_setabs_speaker(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct output_device *device; uint64_t id; int volume; - id = cmd->arg.vol_param.spk_id; - volume = cmd->arg.vol_param.volume; + *retval = 0; + id = cmdarg->vol_param.spk_id; + volume = cmdarg->vol_param.volume; master_volume = volume; @@ -3026,48 +3023,53 @@ volume_setabs_speaker(struct player_command *cmd) #endif if (device->session) - cmd->output_requests_pending = outputs_device_volume_set(device, device_command_cb); + *retval = outputs_device_volume_set(device, device_command_cb);//FIXME Does this need to be += ? } } listener_notify(LISTENER_VOLUME); - if (cmd->output_requests_pending > 0) - return 1; /* async */ + if (*retval > 0) + return COMMAND_PENDING; /* async */ - return 0; + return COMMAND_END; } -static int -repeat_set(struct player_command *cmd) +static enum command_state +repeat_set(void *arg, int *retval) { - if (cmd->arg.mode == repeat) + union player_arg *cmdarg = arg; + + if (cmdarg->mode == repeat) return 0; - switch (cmd->arg.mode) + switch (cmdarg->mode) { case REPEAT_OFF: case REPEAT_SONG: case REPEAT_ALL: - repeat = cmd->arg.mode; + repeat = cmdarg->mode; break; default: - DPRINTF(E_LOG, L_PLAYER, "Invalid repeat mode: %d\n", cmd->arg.mode); - return -1; + DPRINTF(E_LOG, L_PLAYER, "Invalid repeat mode: %d\n", cmdarg->mode); + *retval = -1; + return COMMAND_END; } listener_notify(LISTENER_OPTIONS); - return 0; + *retval = 0; + return COMMAND_END; } -static int -shuffle_set(struct player_command *cmd) +static enum command_state +shuffle_set(void *arg, int *retval) { + union player_arg *cmdarg = arg; uint32_t cur_id; - switch (cmd->arg.intval) + switch (cmdarg->intval) { case 1: if (!shuffle) @@ -3077,28 +3079,31 @@ shuffle_set(struct player_command *cmd) } /* FALLTHROUGH*/ case 0: - shuffle = cmd->arg.intval; + shuffle = cmdarg->intval; break; default: - DPRINTF(E_LOG, L_PLAYER, "Invalid shuffle mode: %d\n", cmd->arg.intval); - return -1; + DPRINTF(E_LOG, L_PLAYER, "Invalid shuffle mode: %d\n", cmdarg->intval); + *retval = -1; + return COMMAND_END; } listener_notify(LISTENER_OPTIONS); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_get_bypos(struct player_command *cmd) +static enum command_state +playerqueue_get_bypos(void *arg, int *retval) { + union player_arg *cmdarg = arg; int count; struct queue *qi; struct player_source *ps; int item_id; - count = cmd->arg.queue_get_param.count; + count = cmdarg->queue_get_param.count; ps = source_now_playing(); @@ -3110,36 +3115,40 @@ playerqueue_get_bypos(struct player_command *cmd) qi = queue_new_bypos(queue, item_id, count, shuffle); - cmd->arg.queue_get_param.queue = qi; + cmdarg->queue_get_param.queue = qi; - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_get_byindex(struct player_command *cmd) +static enum command_state +playerqueue_get_byindex(void *arg, int *retval) { + union player_arg *cmdarg = arg; int pos; int count; struct queue *qi; - pos = cmd->arg.queue_get_param.pos; - count = cmd->arg.queue_get_param.count; + pos = cmdarg->queue_get_param.pos; + count = cmdarg->queue_get_param.count; qi = queue_new_byindex(queue, pos, count, 0); - cmd->arg.queue_get_param.queue = qi; + cmdarg->queue_get_param.queue = qi; - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_add(struct player_command *cmd) +static enum command_state +playerqueue_add(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct queue_item *items; uint32_t cur_id; uint32_t *item_id; - items = cmd->arg.queue_add_param.items; - item_id = cmd->arg.queue_add_param.item_id_ptr; + items = cmdarg->queue_add_param.items; + item_id = cmdarg->queue_add_param.item_id_ptr; queue_add(queue, items); @@ -3157,16 +3166,18 @@ playerqueue_add(struct player_command *cmd) listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_add_next(struct player_command *cmd) +static enum command_state +playerqueue_add_next(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct queue_item *items; uint32_t cur_id; - items = cmd->arg.queue_add_param.items; + items = cmdarg->queue_add_param.items; cur_id = cur_streaming ? cur_streaming->item_id : 0; @@ -3180,17 +3191,19 @@ playerqueue_add_next(struct player_command *cmd) listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_move_bypos(struct player_command *cmd) +static enum command_state +playerqueue_move_bypos(void *arg, int *retval) { + union player_arg *cmdarg = arg; struct player_source *ps_playing; uint32_t item_id; DPRINTF(E_DBG, L_PLAYER, "Moving song from position %d to be the next song after %d\n", - cmd->arg.queue_move_param.from_pos, cmd->arg.queue_move_param.to_pos); + cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos); ps_playing = source_now_playing(); @@ -3202,57 +3215,66 @@ playerqueue_move_bypos(struct player_command *cmd) else item_id = ps_playing->item_id; - queue_move_bypos(queue, item_id, cmd->arg.queue_move_param.from_pos, cmd->arg.queue_move_param.to_pos, shuffle); + queue_move_bypos(queue, item_id, cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos, shuffle); cur_plversion++; listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_move_byindex(struct player_command *cmd) +static enum command_state +playerqueue_move_byindex(void *arg, int *retval) { + union player_arg *cmdarg = arg; + DPRINTF(E_DBG, L_PLAYER, "Moving song from index %d to be the next song after %d\n", - cmd->arg.queue_move_param.from_pos, cmd->arg.queue_move_param.to_pos); + cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos); - queue_move_byindex(queue, cmd->arg.queue_move_param.from_pos, cmd->arg.queue_move_param.to_pos, 0); + queue_move_byindex(queue, cmdarg->queue_move_param.from_pos, cmdarg->queue_move_param.to_pos, 0); cur_plversion++; listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_move_byitemid(struct player_command *cmd) +static enum command_state +playerqueue_move_byitemid(void *arg, int *retval) { + union player_arg *cmdarg = arg; + DPRINTF(E_DBG, L_PLAYER, "Moving song with item-id %d to be the next song after index %d\n", - cmd->arg.queue_move_param.item_id, cmd->arg.queue_move_param.to_pos); + cmdarg->queue_move_param.item_id, cmdarg->queue_move_param.to_pos); - queue_move_byitemid(queue, cmd->arg.queue_move_param.item_id, cmd->arg.queue_move_param.to_pos, 0); + queue_move_byitemid(queue, cmdarg->queue_move_param.item_id, cmdarg->queue_move_param.to_pos, 0); cur_plversion++; listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_remove_bypos(struct player_command *cmd) +static enum command_state +playerqueue_remove_bypos(void *arg, int *retval) { + union player_arg *cmdarg = arg; int pos; struct player_source *ps_playing; uint32_t item_id; - pos = cmd->arg.intval; + pos = cmdarg->intval; if (pos < 1) { DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid position %d\n", pos); - return -1; + *retval = -1; + return COMMAND_END; } ps_playing = source_now_playing(); @@ -3272,18 +3294,20 @@ playerqueue_remove_bypos(struct player_command *cmd) listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_remove_byindex(struct player_command *cmd) +static enum command_state +playerqueue_remove_byindex(void *arg, int *retval) { + union player_arg *cmdarg = arg; int pos; int count; int i; - pos = cmd->arg.queue_remove_param.from_pos; - count = cmd->arg.queue_remove_param.count; + pos = cmdarg->queue_remove_param.from_pos; + count = cmdarg->queue_remove_param.count; DPRINTF(E_DBG, L_PLAYER, "Removing %d items starting from position %d\n", count, pos); @@ -3294,19 +3318,22 @@ playerqueue_remove_byindex(struct player_command *cmd) listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_remove_byitemid(struct player_command *cmd) +static enum command_state +playerqueue_remove_byitemid(void *arg, int *retval) { + union player_arg *cmdarg = arg; uint32_t id; - id = cmd->arg.id; + id = cmdarg->id; if (id < 1) { DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid id %d\n", id); - return -1; + *retval = -1; + return COMMAND_END; } DPRINTF(E_DBG, L_PLAYER, "Removing item with id %d\n", id); @@ -3316,14 +3343,15 @@ playerqueue_remove_byitemid(struct player_command *cmd) listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } /* * Removes all media items from the queue */ -static int -playerqueue_clear(struct player_command *cmd) +static enum command_state +playerqueue_clear(void *arg, int *retval) { queue_clear(queue); @@ -3332,14 +3360,15 @@ playerqueue_clear(struct player_command *cmd) listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } /* * Removes all items from the history */ -static int -playerqueue_clear_history(struct player_command *cmd) +static enum command_state +playerqueue_clear_history(void *arg, int *retval) { memset(history, 0, sizeof(struct player_history)); @@ -3347,149 +3376,31 @@ playerqueue_clear_history(struct player_command *cmd) listener_notify(LISTENER_PLAYLIST); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playerqueue_plid(struct player_command *cmd) +static enum command_state +playerqueue_plid(void *arg, int *retval) { - cur_plid = cmd->arg.id; + union player_arg *cmdarg = arg; + cur_plid = cmdarg->id; - return 0; + *retval = 0; + return COMMAND_END; } -/* Command processing */ -/* Thread: player */ -static void -command_cb(int fd, short what, void *arg) -{ - struct player_command *cmd; - int ret; - - ret = read(cmd_pipe[0], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_PLAYER, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-"); - - goto readd; - } - - if (cmd->nonblock) - { - cmd->func(cmd); - - free(cmd); - goto readd; - } - - pthread_mutex_lock(&cmd->lck); - - cur_cmd = cmd; - - ret = cmd->func(cmd); - - if (ret <= 0) - { - cmd->ret = ret; - - cur_cmd = NULL; - - pthread_cond_signal(&cmd->cond); - pthread_mutex_unlock(&cmd->lck); - } - else - { - /* Command is asynchronous, we don't want to process another command - * before we're done with this one. See command_async_end(). - */ - - return; - } - - readd: - event_add(cmdev, NULL); -} - - -/* Thread: httpd (DACP) - mDNS */ -static int -send_command(struct player_command *cmd) -{ - int ret; - - if (!cmd->func) - { - DPRINTF(E_LOG, L_PLAYER, "BUG: cmd->func is NULL!\n"); - - return -1; - } - - ret = write(cmd_pipe[1], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_PLAYER, "Could not send command: %s\n", strerror(errno)); - - return -1; - } - - return 0; -} - -/* Thread: mDNS */ -static int -nonblock_command(struct player_command *cmd) -{ - int ret; - - ret = send_command(cmd); - if (ret < 0) - return -1; - - return 0; -} - -/* Thread: httpd (DACP) */ -static int -sync_command(struct player_command *cmd) -{ - int ret; - - pthread_mutex_lock(&cmd->lck); - - ret = send_command(cmd); - if (ret < 0) - { - pthread_mutex_unlock(&cmd->lck); - - return -1; - } - - pthread_cond_wait(&cmd->cond, &cmd->lck); - - pthread_mutex_unlock(&cmd->lck); - - ret = cmd->ret; - - return ret; -} /* Player API executed in the httpd (DACP) thread */ int player_get_status(struct player_status *status) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = get_status; - cmd.func_bh = NULL; - cmd.arg.status = status; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.status = status; + ret = commands_exec_sync(cmdbase, get_status, NULL, &cmdarg); return ret; } @@ -3502,45 +3413,32 @@ player_get_status(struct player_status *status) int player_now_playing(uint32_t *id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = now_playing; - cmd.func_bh = NULL; - cmd.arg.id_ptr = id; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.id_ptr = id; + ret = commands_exec_sync(cmdbase, now_playing, NULL, &cmdarg); return ret; } char * player_get_icy_artwork_url(uint32_t id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = artwork_url_get; - cmd.func_bh = NULL; - cmd.arg.icy.id = id; + cmdarg.icy.id = id; if (pthread_self() != tid_player) - ret = sync_command(&cmd); + ret = commands_exec_sync(cmdbase, artwork_url_get, NULL, &cmdarg); else - ret = artwork_url_get(&cmd); - - command_deinit(&cmd); + artwork_url_get(&cmdarg, &ret); if (ret < 0) return NULL; else - return cmd.arg.icy.artwork_url; + return cmdarg.icy.artwork_url; } /* @@ -3559,19 +3457,12 @@ player_get_icy_artwork_url(uint32_t id) int player_playback_start(uint32_t *id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playback_start; - cmd.func_bh = playback_start_bh; - cmd.arg.playback_start_param.id_ptr = id; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.playback_start_param.id_ptr = id; + ret = commands_exec_sync(cmdbase, playback_start, playback_start_bh, &cmdarg); return ret; } @@ -3589,19 +3480,13 @@ player_playback_start(uint32_t *id) int player_playback_start_byindex(int index, uint32_t *id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playback_start_byindex; - cmd.func_bh = playback_start_bh; - cmd.arg.playback_start_param.pos = index; - cmd.arg.playback_start_param.id_ptr = id; - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.playback_start_param.pos = index; + cmdarg.playback_start_param.id_ptr = id; + ret = commands_exec_sync(cmdbase, playback_start_byindex, playback_start_bh, &cmdarg); return ret; } @@ -3621,19 +3506,13 @@ player_playback_start_byindex(int index, uint32_t *id) int player_playback_start_bypos(int pos, uint32_t *id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playback_start_bypos; - cmd.func_bh = playback_start_bh; - cmd.arg.playback_start_param.pos = pos; - cmd.arg.playback_start_param.id_ptr = id; - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.playback_start_param.pos = pos; + cmdarg.playback_start_param.id_ptr = id; + ret = commands_exec_sync(cmdbase, playback_start_bypos, playback_start_bh, &cmdarg); return ret; } @@ -3651,18 +3530,13 @@ player_playback_start_bypos(int pos, uint32_t *id) int player_playback_start_byitemid(uint32_t item_id, uint32_t *id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playback_start_byitemid; - cmd.func_bh = playback_start_bh; - cmd.arg.playback_start_param.id = item_id; - cmd.arg.playback_start_param.id_ptr = id; - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.playback_start_param.id = item_id; + cmdarg.playback_start_param.id_ptr = id; + ret = commands_exec_sync(cmdbase, playback_start_byitemid, playback_start_bh, &cmdarg); + return ret; return ret; } @@ -3670,94 +3544,48 @@ player_playback_start_byitemid(uint32_t item_id, uint32_t *id) int player_playback_stop(void) { - struct player_command cmd; int ret; - command_init(&cmd); - - cmd.func = playback_stop; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - + ret = commands_exec_sync(cmdbase, playback_stop, NULL, NULL); return ret; } int player_playback_pause(void) { - struct player_command cmd; int ret; - command_init(&cmd); - - cmd.func = playback_pause; - cmd.func_bh = playback_pause_bh; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - + ret = commands_exec_sync(cmdbase, playback_pause, playback_pause_bh, NULL); return ret; } int player_playback_seek(int ms) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playback_pause; - cmd.func_bh = playback_seek_bh; - cmd.arg.intval = ms; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.intval = ms; + ret = commands_exec_sync(cmdbase, playback_pause, playback_seek_bh, &cmdarg); return ret; } int player_playback_next(void) { - struct player_command cmd; int ret; - command_init(&cmd); - - cmd.func = playback_pause; - cmd.func_bh = playback_next_bh; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - + ret = commands_exec_sync(cmdbase, playback_pause, playback_next_bh, NULL); return ret; } int player_playback_prev(void) { - struct player_command cmd; int ret; - command_init(&cmd); - - cmd.func = playback_pause; - cmd.func_bh = playback_prev_bh; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - + ret = commands_exec_sync(cmdbase, playback_pause, playback_prev_bh, NULL); return ret; } @@ -3765,136 +3593,89 @@ player_playback_prev(void) void player_speaker_enumerate(spk_enum_cb cb, void *arg) { - struct player_command cmd; + union player_arg cmdarg; struct spk_enum spk_enum; - command_init(&cmd); - spk_enum.cb = cb; spk_enum.arg = arg; - cmd.func = speaker_enumerate; - cmd.func_bh = NULL; - cmd.arg.spk_enum = &spk_enum; + cmdarg.spk_enum = &spk_enum; - sync_command(&cmd); - - command_deinit(&cmd); + commands_exec_sync(cmdbase, speaker_enumerate, NULL, &cmdarg); } int player_speaker_set(uint64_t *ids) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = speaker_set; - cmd.func_bh = NULL; - cmd.arg.device_ids = ids; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.speaker_set_param.device_ids = ids; + cmdarg.speaker_set_param.intval = 0; + ret = commands_exec_sync(cmdbase, speaker_set, NULL, &cmdarg); return ret; } int player_volume_set(int vol) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = volume_set; - cmd.func_bh = NULL; - cmd.arg.intval = vol; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.intval = vol; + ret = commands_exec_sync(cmdbase, volume_set, NULL, &cmdarg); return ret; } int player_volume_setrel_speaker(uint64_t id, int relvol) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = volume_setrel_speaker; - cmd.func_bh = NULL; - cmd.arg.vol_param.spk_id = id; - cmd.arg.vol_param.volume = relvol; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.vol_param.spk_id = id; + cmdarg.vol_param.volume = relvol; + ret = commands_exec_sync(cmdbase, volume_setrel_speaker, NULL, &cmdarg); return ret; } int player_volume_setabs_speaker(uint64_t id, int vol) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = volume_setabs_speaker; - cmd.func_bh = NULL; - cmd.arg.vol_param.spk_id = id; - cmd.arg.vol_param.volume = vol; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.vol_param.spk_id = id; + cmdarg.vol_param.volume = vol; + ret = commands_exec_sync(cmdbase, volume_setabs_speaker, NULL, &cmdarg); return ret; } int player_repeat_set(enum repeat_mode mode) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = repeat_set; - cmd.func_bh = NULL; - cmd.arg.mode = mode; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.mode = mode; + ret = commands_exec_sync(cmdbase, repeat_set, NULL, &cmdarg); return ret; } int player_shuffle_set(int enable) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = shuffle_set; - cmd.func_bh = NULL; - cmd.arg.intval = enable; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.intval = enable; + ret = commands_exec_sync(cmdbase, shuffle_set, NULL, &cmdarg); return ret; } @@ -3910,25 +3691,19 @@ player_shuffle_set(int enable) struct queue * player_queue_get_bypos(int count) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); + cmdarg.queue_get_param.pos = -1; + cmdarg.queue_get_param.count = count; + cmdarg.queue_get_param.queue = NULL; - cmd.func = playerqueue_get_bypos; - cmd.func_bh = NULL; - cmd.arg.queue_get_param.pos = -1; - cmd.arg.queue_get_param.count = count; - cmd.arg.queue_get_param.queue = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + ret = commands_exec_sync(cmdbase, playerqueue_get_bypos, NULL, &cmdarg); if (ret != 0) return NULL; - return cmd.arg.queue_get_param.queue; + return cmdarg.queue_get_param.queue; } /* @@ -3942,25 +3717,19 @@ player_queue_get_bypos(int count) struct queue * player_queue_get_byindex(int index, int count) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); + cmdarg.queue_get_param.pos = index; + cmdarg.queue_get_param.count = count; + cmdarg.queue_get_param.queue = NULL; - cmd.func = playerqueue_get_byindex; - cmd.func_bh = NULL; - cmd.arg.queue_get_param.pos = index; - cmd.arg.queue_get_param.count = count; - cmd.arg.queue_get_param.queue = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + ret = commands_exec_sync(cmdbase, playerqueue_get_byindex, NULL, &cmdarg); if (ret != 0) return NULL; - return cmd.arg.queue_get_param.queue; + return cmdarg.queue_get_param.queue; } /* @@ -3969,20 +3738,13 @@ player_queue_get_byindex(int index, int count) int player_queue_add(struct queue_item *items, uint32_t *item_id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_add; - cmd.func_bh = NULL; - cmd.arg.queue_add_param.items = items; - cmd.arg.queue_add_param.item_id_ptr = item_id; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.queue_add_param.items = items; + cmdarg.queue_add_param.item_id_ptr = item_id; + ret = commands_exec_sync(cmdbase, playerqueue_add, NULL, &cmdarg); return ret; } @@ -3992,19 +3754,12 @@ player_queue_add(struct queue_item *items, uint32_t *item_id) int player_queue_add_next(struct queue_item *items) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_add_next; - cmd.func_bh = NULL; - cmd.arg.queue_add_param.items = items; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.queue_add_param.items = items; + ret = commands_exec_sync(cmdbase, playerqueue_add_next, NULL, &cmdarg); return ret; } @@ -4017,60 +3772,39 @@ player_queue_add_next(struct queue_item *items) int player_queue_move_bypos(int pos_from, int pos_to) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_move_bypos; - cmd.func_bh = NULL; - cmd.arg.queue_move_param.from_pos = pos_from; - cmd.arg.queue_move_param.to_pos = pos_to; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.queue_move_param.from_pos = pos_from; + cmdarg.queue_move_param.to_pos = pos_to; + ret = commands_exec_sync(cmdbase, playerqueue_move_bypos, NULL, &cmdarg); return ret; } int player_queue_move_byindex(int pos_from, int pos_to) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_move_byindex; - cmd.func_bh = NULL; - cmd.arg.queue_move_param.from_pos = pos_from; - cmd.arg.queue_move_param.to_pos = pos_to; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.queue_move_param.from_pos = pos_from; + cmdarg.queue_move_param.to_pos = pos_to; + ret = commands_exec_sync(cmdbase, playerqueue_move_byindex, NULL, &cmdarg); return ret; } int player_queue_move_byitemid(uint32_t item_id, int pos_to) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_move_byitemid; - cmd.func_bh = NULL; - cmd.arg.queue_move_param.item_id = item_id; - cmd.arg.queue_move_param.to_pos = pos_to; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.queue_move_param.item_id = item_id; + cmdarg.queue_move_param.to_pos = pos_to; + ret = commands_exec_sync(cmdbase, playerqueue_move_byitemid, NULL, &cmdarg); return ret; } @@ -4086,19 +3820,12 @@ player_queue_move_byitemid(uint32_t item_id, int pos_to) int player_queue_remove_bypos(int pos) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_remove_bypos; - cmd.func_bh = NULL; - cmd.arg.intval = pos; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.intval = pos; + ret = commands_exec_sync(cmdbase, playerqueue_remove_bypos, NULL, &cmdarg); return ret; } @@ -4114,20 +3841,13 @@ player_queue_remove_bypos(int pos) int player_queue_remove_byindex(int pos, int count) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_remove_byindex; - cmd.func_bh = NULL; - cmd.arg.queue_remove_param.from_pos = pos; - cmd.arg.queue_remove_param.count = count; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.queue_remove_param.from_pos = pos; + cmdarg.queue_remove_param.count = count; + ret = commands_exec_sync(cmdbase, playerqueue_remove_byindex, NULL, &cmdarg); return ret; } @@ -4140,141 +3860,85 @@ player_queue_remove_byindex(int pos, int count) int player_queue_remove_byitemid(uint32_t id) { - struct player_command cmd; + union player_arg cmdarg; int ret; - command_init(&cmd); - - cmd.func = playerqueue_remove_byitemid; - cmd.func_bh = NULL; - cmd.arg.id = id; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + cmdarg.id = id; + ret = commands_exec_sync(cmdbase, playerqueue_remove_byitemid, NULL, &cmdarg); return ret; } void player_queue_clear(void) { - struct player_command cmd; - - command_init(&cmd); - - cmd.func = playerqueue_clear; - cmd.func_bh = NULL; - cmd.arg.noarg = NULL; - - sync_command(&cmd); - - command_deinit(&cmd); + commands_exec_sync(cmdbase, playerqueue_clear, NULL, NULL); } void player_queue_clear_history() { - struct player_command cmd; - - command_init(&cmd); - - cmd.func = playerqueue_clear_history; - cmd.func_bh = NULL; - - sync_command(&cmd); - - command_deinit(&cmd); + commands_exec_sync(cmdbase, playerqueue_clear_history, NULL, NULL); } void player_queue_plid(uint32_t plid) { - struct player_command cmd; + union player_arg cmdarg; - command_init(&cmd); + cmdarg.id = plid; - cmd.func = playerqueue_plid; - cmd.func_bh = NULL; - cmd.arg.id = plid; - - sync_command(&cmd); - - command_deinit(&cmd); + commands_exec_sync(cmdbase, playerqueue_plid, NULL, &cmdarg); } /* Non-blocking commands used by mDNS */ int player_device_add(void *device) { - struct player_command *cmd; + union player_arg *cmdarg; int ret; - cmd = calloc(1, sizeof(struct player_command)); - if (!cmd) + cmdarg = calloc(1, sizeof(union player_arg)); + if (!cmdarg) { DPRINTF(E_LOG, L_PLAYER, "Could not allocate player_command\n"); return -1; } - cmd->nonblock = 1; + cmdarg->device = device; - cmd->func = device_add; - cmd->arg.device = device; - - ret = nonblock_command(cmd); - if (ret < 0) - { - free(cmd); - return -1; - } - - return 0; + ret = commands_exec_async(cmdbase, device_add, cmdarg); + return ret; } int player_device_remove(void *device) { - struct player_command *cmd; + union player_arg *cmdarg; int ret; - cmd = calloc(1, sizeof(struct player_command)); - if (!cmd) + cmdarg = calloc(1, sizeof(union player_arg)); + if (!cmdarg) { DPRINTF(E_LOG, L_PLAYER, "Could not allocate player_command\n"); return -1; } - cmd->nonblock = 1; + cmdarg->device = device; - cmd->func = device_remove_family; - cmd->arg.device = device; - - ret = nonblock_command(cmd); - if (ret < 0) - { - free(cmd); - return -1; - } - - return 0; + ret = commands_exec_async(cmdbase, device_remove_family, cmdarg); + return ret; } /* Thread: worker */ static void player_metadata_send(struct player_metadata *pmd) { - struct player_command cmd; + union player_arg cmdarg; - command_init(&cmd); + cmdarg.pmd = pmd; - cmd.func = metadata_send; - cmd.func_bh = NULL; - cmd.arg.pmd = pmd; - - sync_command(&cmd); - - command_deinit(&cmd); + commands_exec_sync(cmdbase, metadata_send, NULL, &cmdarg); } /* Thread: player */ @@ -4339,8 +4003,6 @@ player_init(void) output_sessions = 0; - cur_cmd = NULL; - cur_playing = NULL; cur_streaming = NULL; cur_plid = 0; @@ -4415,18 +4077,6 @@ player_init(void) goto exit_fail; } -#ifdef HAVE_PIPE2 - ret = pipe2(cmd_pipe, O_CLOEXEC); -#else - ret = pipe(cmd_pipe); -#endif - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Could not create command pipe: %s\n", strerror(errno)); - - goto cmd_fail; - } - evbase_player = event_base_new(); if (!evbase_player) { @@ -4442,13 +4092,6 @@ player_init(void) goto evnew_fail; } - cmdev = event_new(evbase_player, cmd_pipe[0], EV_READ, command_cb, NULL); - if (!cmdev) - { - DPRINTF(E_LOG, L_PLAYER, "Could not create cmd event\n"); - goto evnew_fail; - } - #if defined(__linux__) pb_timer_ev = event_new(evbase_player, pb_timer_fd, EV_READ | EV_PERSIST, player_playback_cb, NULL); #else @@ -4461,9 +4104,10 @@ player_init(void) } event_add(exitev, NULL); - event_add(cmdev, NULL); event_add(pb_timer_ev, NULL); + cmdbase = commands_base_new(evbase_player); + ret = outputs_init(); if (ret < 0) { @@ -4488,12 +4132,10 @@ player_init(void) thread_fail: outputs_deinit(); outputs_fail: + commands_base_free(cmdbase); evnew_fail: event_base_free(evbase_player); evbase_fail: - close(cmd_pipe[0]); - close(cmd_pipe[1]); - cmd_fail: close(exit_pipe[0]); close(exit_pipe[1]); exit_fail: @@ -4545,11 +4187,9 @@ player_deinit(void) outputs_deinit(); + event_base_free(evbase_player); + + commands_base_free(cmdbase); close(exit_pipe[0]); close(exit_pipe[1]); - close(cmd_pipe[0]); - close(cmd_pipe[1]); - cmd_pipe[0] = -1; - cmd_pipe[1] = -1; - event_base_free(evbase_player); } diff --git a/src/spotify.c b/src/spotify.c index 61870a1b..8bd3c3f9 100644 --- a/src/spotify.c +++ b/src/spotify.c @@ -49,6 +49,7 @@ #include "conffile.h" #include "filescanner.h" #include "cache.h" +#include "commands.h" /* How long to wait for audio (in sec) before giving up */ @@ -102,30 +103,6 @@ struct artwork_get_param int is_loaded; }; -struct spotify_command; - -typedef int (*cmd_func)(struct spotify_command *cmd); - -struct spotify_command -{ - pthread_mutex_t lck; - pthread_cond_t cond; - - cmd_func func; - cmd_func func_bh; - - int nonblock; - - union { - void *noarg; - sp_link *link; - int seek_ms; - struct audio_get_param audio; - struct artwork_get_param artwork; - } arg; - - int ret; -}; /* --- Globals --- */ // Spotify thread @@ -138,20 +115,18 @@ static pthread_cond_t login_cond; // Event base, pipes and events struct event_base *evbase_spotify; static int g_exit_pipe[2]; -static int g_cmd_pipe[2]; static int g_notify_pipe[2]; static struct event *g_exitev; -static struct event *g_cmdev; static struct event *g_notifyev; +static struct commands_base *cmdbase; + // The global session handle static sp_session *g_sess; // The global library handle static void *g_libhandle; // The global state telling us what the thread is currently doing static enum spotify_state g_state; -/* (not used) Tells which commmand is currently being processed */ -static struct spotify_command *g_cmd; // The global base playlist id (parent of all Spotify playlists in the db) static int g_base_plid; @@ -411,77 +386,6 @@ fptr_assign_all() /* ---------------------------- COMMAND EXECUTION -------------------------- */ -static void -command_init(struct spotify_command *cmd) -{ - memset(cmd, 0, sizeof(struct spotify_command)); - - pthread_mutex_init(&cmd->lck, NULL); - pthread_cond_init(&cmd->cond, NULL); -} - -static void -command_deinit(struct spotify_command *cmd) -{ - pthread_cond_destroy(&cmd->cond); - pthread_mutex_destroy(&cmd->lck); -} - -static int -send_command(struct spotify_command *cmd) -{ - int ret; - - if (!cmd->func) - { - DPRINTF(E_LOG, L_SPOTIFY, "BUG: cmd->func is NULL!\n"); - return -1; - } - - ret = write(g_cmd_pipe[1], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not send command: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int -sync_command(struct spotify_command *cmd) -{ - int ret; - - pthread_mutex_lock(&cmd->lck); - - ret = send_command(cmd); - if (ret < 0) - { - pthread_mutex_unlock(&cmd->lck); - return -1; - } - - pthread_cond_wait(&cmd->cond, &cmd->lck); - pthread_mutex_unlock(&cmd->lck); - - ret = cmd->ret; - - return ret; -} - -static int -nonblock_command(struct spotify_command *cmd) -{ - int ret; - - ret = send_command(cmd); - if (ret < 0) - return -1; - - return 0; -} - /* Thread: main and filescanner */ static void thread_exit(void) @@ -1033,47 +937,55 @@ audio_fifo_flush(void) pthread_mutex_unlock(&g_audio_fifo->mutex); } -static int -playback_setup(struct spotify_command *cmd) +static enum command_state +playback_setup(void *arg, int *retval) { + sp_link *link; sp_track *track; sp_error err; DPRINTF(E_DBG, L_SPOTIFY, "Setting up for playback\n"); + link = (sp_link *) arg; + if (SP_CONNECTION_STATE_LOGGED_IN != fptr_sp_session_connectionstate(g_sess)) { DPRINTF(E_LOG, L_SPOTIFY, "Can't play music, not connected and logged in to Spotify\n"); - return -1; + *retval = -1; + return COMMAND_END; } - if (!cmd->arg.link) + if (!link) { DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed, no Spotify link\n"); - return -1; + *retval = -1; + return COMMAND_END; } - track = fptr_sp_link_as_track(cmd->arg.link); + track = fptr_sp_link_as_track(link); if (!track) { DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed, invalid Spotify track\n"); - return -1; + *retval = -1; + return COMMAND_END; } err = fptr_sp_session_player_load(g_sess, track); if (SP_ERROR_OK != err) { DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed: %s\n", fptr_sp_error_message(err)); - return -1; + *retval = -1; + return COMMAND_END; } audio_fifo_flush(); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_play(struct spotify_command *cmd) +static enum command_state +playback_play(void *arg, int *retval) { sp_error err; @@ -1083,16 +995,18 @@ playback_play(struct spotify_command *cmd) if (SP_ERROR_OK != err) { DPRINTF(E_LOG, L_SPOTIFY, "Playback failed: %s\n", fptr_sp_error_message(err)); - return -1; + *retval = -1; + return COMMAND_END; } g_state = SPOTIFY_STATE_PLAYING; - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_pause(struct spotify_command *cmd) +static enum command_state +playback_pause(void *arg, int *retval) { sp_error err; @@ -1104,16 +1018,18 @@ playback_pause(struct spotify_command *cmd) if (SP_ERROR_OK != err) { DPRINTF(E_LOG, L_SPOTIFY, "Playback pause failed: %s\n", fptr_sp_error_message(err)); - return -1; + *retval = -1; + return COMMAND_END; } g_state = SPOTIFY_STATE_PAUSED; - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_stop(struct spotify_command *cmd) +static enum command_state +playback_stop(void *arg, int *retval) { sp_error err; @@ -1123,35 +1039,43 @@ playback_stop(struct spotify_command *cmd) if (SP_ERROR_OK != err) { DPRINTF(E_LOG, L_SPOTIFY, "Playback stop failed: %s\n", fptr_sp_error_message(err)); - return -1; + *retval = -1; + return COMMAND_END; } g_state = SPOTIFY_STATE_STOPPED; - return 0; + + *retval = 0; + return COMMAND_END; } -static int -playback_seek(struct spotify_command *cmd) +static enum command_state +playback_seek(void *arg, int *retval) { + int seek_ms; sp_error err; DPRINTF(E_DBG, L_SPOTIFY, "Playback seek\n"); - err = fptr_sp_session_player_seek(g_sess, cmd->arg.seek_ms); + seek_ms = *((int *) arg); + + err = fptr_sp_session_player_seek(g_sess, seek_ms); if (SP_ERROR_OK != err) { DPRINTF(E_LOG, L_SPOTIFY, "Could not seek: %s\n", fptr_sp_error_message(err)); - return -1; + *retval = -1; + return COMMAND_END; } audio_fifo_flush(); - return 0; + *retval = 0; + return COMMAND_END; } -static int -playback_eot(struct spotify_command *cmd) +static enum command_state +playback_eot(void *arg, int *retval) { sp_error err; @@ -1166,12 +1090,14 @@ playback_eot(struct spotify_command *cmd) g_state = SPOTIFY_STATE_STOPPING; - return 0; + *retval = 0; + return COMMAND_END; } -static int -audio_get(struct spotify_command *cmd) +static enum command_state +audio_get(void *arg, int *retval) { + struct audio_get_param *audio; struct timespec ts; audio_fifo_data_t *afd; int processed; @@ -1179,16 +1105,17 @@ audio_get(struct spotify_command *cmd) int ret; int s; + audio = (struct audio_get_param *) arg; afd = NULL; processed = 0; // If spotify was paused begin by resuming playback if (g_state == SPOTIFY_STATE_PAUSED) - playback_play(NULL); + playback_play(NULL, retval); pthread_mutex_lock(&g_audio_fifo->mutex); - while ((processed < cmd->arg.audio.wanted) && (g_state != SPOTIFY_STATE_STOPPED)) + while ((processed < audio->wanted) && (g_state != SPOTIFY_STATE_STOPPED)) { // If track has ended and buffer is empty if ((g_state == SPOTIFY_STATE_STOPPING) && (g_audio_fifo->qlen <= 0)) @@ -1227,14 +1154,15 @@ audio_get(struct spotify_command *cmd) s = afd->nsamples * sizeof(int16_t) * 2; - ret = evbuffer_add(cmd->arg.audio.evbuf, afd->samples, s); + ret = evbuffer_add(audio->evbuf, afd->samples, s); free(afd); afd = NULL; if (ret < 0) { DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for evbuffer (tried to add %d bytes)\n", s); pthread_mutex_unlock(&g_audio_fifo->mutex); - return -1; + *retval = -1; + return COMMAND_END; } processed += s; @@ -1242,41 +1170,39 @@ audio_get(struct spotify_command *cmd) pthread_mutex_unlock(&g_audio_fifo->mutex); - return processed; + + *retval = processed; + return COMMAND_END; } static void artwork_loaded_cb(sp_image *image, void *userdata) { - struct spotify_command *cmd = userdata; + struct artwork_get_param *artwork; + + artwork = userdata; + + pthread_mutex_lock(&artwork->mutex); - pthread_mutex_lock(&cmd->arg.artwork.mutex); + artwork->is_loaded = 1; - cmd->arg.artwork.is_loaded = 1; - - pthread_cond_signal(&cmd->arg.artwork.cond); - pthread_mutex_unlock(&cmd->arg.artwork.mutex); + pthread_cond_signal(&artwork->cond); + pthread_mutex_unlock(&artwork->mutex); } -static int -artwork_get_bh(struct spotify_command *cmd) +static enum command_state +artwork_get_bh(void *arg, int *retval) { + struct artwork_get_param *artwork; sp_imageformat imageformat; sp_error err; const void *data; size_t data_size; int ret; - sp_image *image = cmd->arg.artwork.image; - char *path = cmd->arg.artwork.path; - - if (!cmd->arg.artwork.is_loaded) - { - DPRINTF(E_DBG, L_SPOTIFY, "Request for artwork timed out: %s\n", path); - - fptr_sp_image_remove_load_callback(image, artwork_loaded_cb, cmd); - goto fail; - } + artwork = arg; + sp_image *image = artwork->image; + char *path = artwork->path; err = fptr_sp_image_error(image); if (err != SP_ERROR_OK) @@ -1305,14 +1231,14 @@ artwork_get_bh(struct spotify_command *cmd) goto fail; } - ret = evbuffer_expand(cmd->arg.artwork.evbuf, data_size); + ret = evbuffer_expand(artwork->evbuf, data_size); if (ret < 0) { DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for artwork\n"); goto fail; } - ret = evbuffer_add(cmd->arg.artwork.evbuf, data, data_size); + ret = evbuffer_add(artwork->evbuf, data, data_size); if (ret < 0) { DPRINTF(E_LOG, L_SPOTIFY, "Could not add Spotify image to event buffer\n"); @@ -1323,17 +1249,20 @@ artwork_get_bh(struct spotify_command *cmd) fptr_sp_image_release(image); - return data_size; + *retval = 0; + return COMMAND_END; fail: fptr_sp_image_release(image); - return -1; + *retval = -1; + return COMMAND_END; } -static int -artwork_get(struct spotify_command *cmd) +static enum command_state +artwork_get(void *arg, int *retval) { + struct artwork_get_param *artwork; char *path; sp_link *link; sp_track *track; @@ -1343,7 +1272,8 @@ artwork_get(struct spotify_command *cmd) sp_image_size image_size; sp_error err; - path = cmd->arg.artwork.path; + artwork = arg; + path = artwork->path; // Now begins: path -> link -> track -> album -> image_id -> image -> format -> data link = fptr_sp_link_create_from_string(path); @@ -1369,9 +1299,9 @@ artwork_get(struct spotify_command *cmd) // Get an image at least the same size as requested image_size = SP_IMAGE_SIZE_SMALL; // 64x64 - if ((cmd->arg.artwork.max_w > 64) || (cmd->arg.artwork.max_h > 64)) + if ((artwork->max_w > 64) || (artwork->max_h > 64)) image_size = SP_IMAGE_SIZE_NORMAL; // 300x300 - if ((cmd->arg.artwork.max_w > 300) || (cmd->arg.artwork.max_h > 300)) + if ((artwork->max_w > 300) || (artwork->max_h > 300)) image_size = SP_IMAGE_SIZE_LARGE; // 640x640 image_id = fptr_sp_album_cover(album, image_size); @@ -1390,31 +1320,35 @@ artwork_get(struct spotify_command *cmd) fptr_sp_link_release(link); - cmd->arg.artwork.image = image; + artwork->image = image; + artwork->is_loaded = fptr_sp_image_is_loaded(image); /* If the image is ready we can return it straight away, otherwise we will * let the calling thread wait, since the Spotify thread should not wait */ - if ( (cmd->arg.artwork.is_loaded = fptr_sp_image_is_loaded(image)) ) - return artwork_get_bh(cmd); + if (artwork->is_loaded) + return artwork_get_bh(artwork, retval); DPRINTF(E_SPAM, L_SPOTIFY, "Will wait for Spotify to call artwork_loaded_cb\n"); /* Async - we will return to spotify_artwork_get which will wait for callback */ - err = fptr_sp_image_add_load_callback(image, artwork_loaded_cb, cmd); + err = fptr_sp_image_add_load_callback(image, artwork_loaded_cb, artwork); if (err != SP_ERROR_OK) { DPRINTF(E_WARN, L_SPOTIFY, "Adding artwork cb failed, Spotify error: %s\n", fptr_sp_error_message(err)); - return -1; + *retval = -1; + return COMMAND_END; } - return 0; + *retval = 0; + return COMMAND_END; level2_exit: fptr_sp_link_release(link); level1_exit: - return -1; + *retval = -1; + return COMMAND_END; } @@ -1615,24 +1549,9 @@ static void connectionstate_updated(sp_session *session) */ static void end_of_track(sp_session *sess) { - struct spotify_command *cmd; - DPRINTF(E_DBG, L_SPOTIFY, "End of track\n"); - cmd = (struct spotify_command *)malloc(sizeof(struct spotify_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not allocate spotify_command\n"); - return; - } - memset(cmd, 0, sizeof(struct spotify_command)); - - cmd->nonblock = 1; - - cmd->func = playback_eot; - cmd->arg.noarg = NULL; - - nonblock_command(cmd); + commands_exec_async(cmdbase, playback_eot, NULL); } /** @@ -1719,41 +1638,6 @@ exit_cb(int fd, short what, void *arg) event_add(g_exitev, NULL); } -static void -command_cb(int fd, short what, void *arg) -{ - struct spotify_command *cmd; - int ret; - - ret = read(g_cmd_pipe[0], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-"); - goto readd; - } - - if (cmd->nonblock) - { - cmd->func(cmd); - - free(cmd); - goto readd; - } - - pthread_mutex_lock(&cmd->lck); - - g_cmd = cmd; - ret = cmd->func(cmd); - cmd->ret = ret; - g_cmd = NULL; - - pthread_cond_signal(&cmd->cond); - pthread_mutex_unlock(&cmd->lck); - - readd: - event_add(g_cmdev, NULL); -} - /* Process events when timeout expires or triggered by libspotify's notify_main_thread */ static void notify_cb(int fd, short what, void *arg) @@ -1789,9 +1673,7 @@ notify_cb(int fd, short what, void *arg) int spotify_playback_setup(struct media_file_info *mfi) { - struct spotify_command cmd; sp_link *link; - int ret; DPRINTF(E_DBG, L_SPOTIFY, "Playback setup request\n"); @@ -1802,144 +1684,59 @@ spotify_playback_setup(struct media_file_info *mfi) return -1; } - command_init(&cmd); - - cmd.func = playback_setup; - cmd.arg.link = link; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, playback_setup, NULL, link); } int spotify_playback_play() { - struct spotify_command cmd; - int ret; - DPRINTF(E_DBG, L_SPOTIFY, "Playback request\n"); - command_init(&cmd); - - cmd.func = playback_play; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, playback_play, NULL, NULL); } int spotify_playback_pause() { - struct spotify_command cmd; - int ret; - DPRINTF(E_DBG, L_SPOTIFY, "Pause request\n"); - command_init(&cmd); - - cmd.func = playback_pause; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, playback_pause, NULL, NULL); } /* Thread: libspotify */ void spotify_playback_pause_nonblock(void) { - struct spotify_command *cmd; - DPRINTF(E_DBG, L_SPOTIFY, "Nonblock pause request\n"); - cmd = (struct spotify_command *)malloc(sizeof(struct spotify_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not allocate spotify_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct spotify_command)); - - cmd->nonblock = 1; - - cmd->func = playback_pause; - cmd->arg.noarg = NULL; - - nonblock_command(cmd); + commands_exec_async(cmdbase, playback_pause, NULL); } /* Thread: player and libspotify */ int spotify_playback_stop(void) { - struct spotify_command cmd; - int ret; - DPRINTF(E_DBG, L_SPOTIFY, "Stop request\n"); - command_init(&cmd); - - cmd.func = playback_stop; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, playback_stop, NULL, NULL); } /* Thread: player and libspotify */ void spotify_playback_stop_nonblock(void) { - struct spotify_command *cmd; - DPRINTF(E_DBG, L_SPOTIFY, "Nonblock stop request\n"); - cmd = (struct spotify_command *)malloc(sizeof(struct spotify_command)); - if (!cmd) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not allocate spotify_command\n"); - return; - } - - memset(cmd, 0, sizeof(struct spotify_command)); - - cmd->nonblock = 1; - - cmd->func = playback_stop; - cmd->arg.noarg = NULL; - - nonblock_command(cmd); + commands_exec_async(cmdbase, playback_stop, NULL); } /* Thread: player */ int spotify_playback_seek(int ms) { - struct spotify_command cmd; int ret; - command_init(&cmd); - - cmd.func = playback_seek; - cmd.arg.seek_ms = ms; - - ret = sync_command(&cmd); - - command_deinit(&cmd); + ret = commands_exec_sync(cmdbase, playback_seek, NULL, &ms); if (ret == 0) return ms; @@ -1951,58 +1748,44 @@ spotify_playback_seek(int ms) int spotify_audio_get(struct evbuffer *evbuf, int wanted) { - struct spotify_command cmd; - int ret; + struct audio_get_param audio; - command_init(&cmd); + audio.evbuf = evbuf; + audio.wanted = wanted; - cmd.func = audio_get; - cmd.arg.audio.evbuf = evbuf; - cmd.arg.audio.wanted = wanted; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; + return commands_exec_sync(cmdbase, audio_get, NULL, &audio); } /* Thread: httpd (artwork) and worker */ int spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h) { - struct spotify_command cmd; + struct artwork_get_param artwork; struct timespec ts; int ret; - command_init(&cmd); + artwork.evbuf = evbuf; + artwork.path = path; + artwork.max_w = max_w; + artwork.max_h = max_h; - cmd.func = artwork_get; - cmd.arg.artwork.evbuf = evbuf; - cmd.arg.artwork.path = path; - cmd.arg.artwork.max_w = max_w; - cmd.arg.artwork.max_h = max_h; - - pthread_mutex_init(&cmd.arg.artwork.mutex, NULL); - pthread_cond_init(&cmd.arg.artwork.cond, NULL); - - ret = sync_command(&cmd); + pthread_mutex_init(&artwork.mutex, NULL); + pthread_cond_init(&artwork.cond, NULL); + ret = commands_exec_sync(cmdbase, artwork_get, NULL, &artwork); + // Artwork was not ready, wait for callback from libspotify if (ret == 0) { - pthread_mutex_lock(&cmd.arg.artwork.mutex); + pthread_mutex_lock(&artwork.mutex); mk_reltime(&ts, SPOTIFY_ARTWORK_TIMEOUT); - if (!cmd.arg.artwork.is_loaded) - pthread_cond_timedwait(&cmd.arg.artwork.cond, &cmd.arg.artwork.mutex, &ts); - pthread_mutex_unlock(&cmd.arg.artwork.mutex); + if (!artwork.is_loaded) + pthread_cond_timedwait(&artwork.cond, &artwork.mutex, &ts); + pthread_mutex_unlock(&artwork.mutex); - cmd.func = artwork_get_bh; - ret = sync_command(&cmd); + ret = commands_exec_sync(cmdbase, artwork_get_bh, NULL, &artwork); } - - command_deinit(&cmd); - + return ret; } @@ -2204,17 +1987,6 @@ spotify_init(void) goto exit_fail; } -#ifdef HAVE_PIPE2 - ret = pipe2(g_cmd_pipe, O_CLOEXEC); -#else - ret = pipe(g_cmd_pipe); -#endif - if (ret < 0) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not create command pipe: %s\n", strerror(errno)); - goto cmd_fail; - } - #ifdef HAVE_PIPE2 ret = pipe2(g_notify_pipe, O_CLOEXEC); #else @@ -2240,13 +2012,6 @@ spotify_init(void) goto evnew_fail; } - g_cmdev = event_new(evbase_spotify, g_cmd_pipe[0], EV_READ, command_cb, NULL); - if (!g_cmdev) - { - DPRINTF(E_LOG, L_SPOTIFY, "Could not create cmd event\n"); - goto evnew_fail; - } - g_notifyev = event_new(evbase_spotify, g_notify_pipe[0], EV_READ | EV_TIMEOUT, notify_cb, NULL); if (!g_notifyev) { @@ -2255,9 +2020,16 @@ spotify_init(void) } event_add(g_exitev, NULL); - event_add(g_cmdev, NULL); event_add(g_notifyev, NULL); + + cmdbase = commands_base_new(evbase_spotify); + if (!cmdbase) + { + DPRINTF(E_LOG, L_SPOTIFY, "Could not create command base\n"); + goto cmd_fail; + } + DPRINTF(E_INFO, L_SPOTIFY, "Spotify session init\n"); spotify_cfg = cfg_getsec(cfg, "spotify"); @@ -2333,7 +2105,9 @@ spotify_init(void) g_sess = NULL; session_fail: + cmd_fail: evnew_fail: + commands_base_free(cmdbase); event_base_free(evbase_spotify); evbase_spotify = NULL; @@ -2342,10 +2116,6 @@ spotify_init(void) close(g_notify_pipe[1]); notify_fail: - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); - - cmd_fail: close(g_exit_pipe[0]); close(g_exit_pipe[1]); @@ -2385,11 +2155,10 @@ spotify_deinit(void) /* Free event base (should free events too) */ event_base_free(evbase_spotify); - /* Close pipes */ + /* Close pipes and free command base */ + commands_base_free(cmdbase); close(g_notify_pipe[0]); close(g_notify_pipe[1]); - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); close(g_exit_pipe[0]); close(g_exit_pipe[1]); diff --git a/src/worker.c b/src/worker.c index da6ba097..6bb90d73 100644 --- a/src/worker.c +++ b/src/worker.c @@ -39,29 +39,15 @@ #include "db.h" #include "logger.h" #include "worker.h" +#include "commands.h" -struct worker_command; - -typedef int (*cmd_func)(struct worker_command *cmd); - -struct worker_command +struct worker_arg { - pthread_mutex_t lck; - pthread_cond_t cond; - - cmd_func func; - - int nonblock; - - struct { - void (*cb)(void *); - void *cb_arg; - int delay; - struct event *timer; - } arg; - - int ret; + void (*cb)(void *); + void *cb_arg; + int delay; + struct event *timer; }; @@ -73,9 +59,9 @@ static pthread_t tid_worker; struct event_base *evbase_worker; static int g_initialized; static int g_exit_pipe[2]; -static int g_cmd_pipe[2]; static struct event *g_exitev; -static struct event *g_cmdev; +static struct commands_base *cmdbase; + /* ---------------------------- CALLBACK EXECUTION ------------------------- */ /* Thread: worker */ @@ -83,71 +69,39 @@ static struct event *g_cmdev; static void execute_cb(int fd, short what, void *arg) { - struct worker_command *cmd = arg; + struct worker_arg *cmdarg = arg; - cmd->arg.cb(cmd->arg.cb_arg); + cmdarg->cb(cmdarg->cb_arg); - event_free(cmd->arg.timer); - free(cmd->arg.cb_arg); - free(cmd); + event_free(cmdarg->timer); + free(cmdarg->cb_arg); + free(cmdarg); } -static int -execute(struct worker_command *cmd) +static enum command_state +execute(void *arg, int *retval) { - struct timeval tv = { cmd->arg.delay, 0 }; + struct worker_arg *cmdarg = arg; + struct timeval tv = { cmdarg->delay, 0 }; - if (cmd->arg.delay) + if (cmdarg->delay) { - cmd->arg.timer = evtimer_new(evbase_worker, execute_cb, cmd); - evtimer_add(cmd->arg.timer, &tv); + cmdarg->timer = evtimer_new(evbase_worker, execute_cb, cmdarg); + evtimer_add(cmdarg->timer, &tv); - return 1; // Not done yet, ask caller not to free cmd + *retval = 0; + return COMMAND_PENDING; // Not done yet, ask caller not to free cmd } - cmd->arg.cb(cmd->arg.cb_arg); - free(cmd->arg.cb_arg); + cmdarg->cb(cmdarg->cb_arg); + free(cmdarg->cb_arg); - return 0; + *retval = 0; + return COMMAND_END; } -/* ---------------------------- COMMAND EXECUTION -------------------------- */ - -static int -send_command(struct worker_command *cmd) -{ - int ret; - - if (!cmd->func) - { - DPRINTF(E_LOG, L_MAIN, "BUG: cmd->func is NULL!\n"); - return -1; - } - - ret = write(g_cmd_pipe[1], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_MAIN, "Could not send command: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int -nonblock_command(struct worker_command *cmd) -{ - int ret; - - ret = send_command(cmd); - if (ret < 0) - return -1; - - return 0; -} - /* Thread: main */ static void thread_exit(void) @@ -209,40 +163,6 @@ exit_cb(int fd, short what, void *arg) event_add(g_exitev, NULL); } -static void -command_cb(int fd, short what, void *arg) -{ - struct worker_command *cmd; - int ret; - - ret = read(g_cmd_pipe[0], &cmd, sizeof(cmd)); - if (ret != sizeof(cmd)) - { - DPRINTF(E_LOG, L_MAIN, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-"); - goto readd; - } - - if (cmd->nonblock) - { - ret = cmd->func(cmd); - - if (ret == 0) - free(cmd); - goto readd; - } - - pthread_mutex_lock(&cmd->lck); - - ret = cmd->func(cmd); - cmd->ret = ret; - - pthread_cond_signal(&cmd->cond); - pthread_mutex_unlock(&cmd->lck); - - readd: - event_add(g_cmdev, NULL); -} - /* ---------------------------- Our worker API --------------------------- */ @@ -250,19 +170,19 @@ command_cb(int fd, short what, void *arg) void worker_execute(void (*cb)(void *), void *cb_arg, size_t arg_size, int delay) { - struct worker_command *cmd; + struct worker_arg *cmdarg; void *argcpy; DPRINTF(E_DBG, L_MAIN, "Got worker execute request\n"); - cmd = (struct worker_command *)malloc(sizeof(struct worker_command)); - if (!cmd) + cmdarg = (struct worker_arg *)malloc(sizeof(struct worker_arg)); + if (!cmdarg) { - DPRINTF(E_LOG, L_MAIN, "Could not allocate worker_command\n"); + DPRINTF(E_LOG, L_MAIN, "Could not allocate worker_arg\n"); return; } - memset(cmd, 0, sizeof(struct worker_command)); + memset(cmdarg, 0, sizeof(struct worker_arg)); argcpy = malloc(arg_size); if (!argcpy) @@ -273,15 +193,11 @@ worker_execute(void (*cb)(void *), void *cb_arg, size_t arg_size, int delay) memcpy(argcpy, cb_arg, arg_size); - cmd->nonblock = 1; - cmd->func = execute; - cmd->arg.cb = cb; - cmd->arg.cb_arg = argcpy; - cmd->arg.delay = delay; + cmdarg->cb = cb; + cmdarg->cb_arg = argcpy; + cmdarg->delay = delay; - nonblock_command(cmd); - - return; + commands_exec_async(cmdbase, execute, cmdarg); } int @@ -300,17 +216,6 @@ worker_init(void) goto exit_fail; } -#ifdef HAVE_PIPE2 - ret = pipe2(g_cmd_pipe, O_CLOEXEC); -#else - ret = pipe(g_cmd_pipe); -#endif - if (ret < 0) - { - DPRINTF(E_LOG, L_MAIN, "Could not create command pipe: %s\n", strerror(errno)); - goto cmd_fail; - } - evbase_worker = event_base_new(); if (!evbase_worker) { @@ -325,15 +230,9 @@ worker_init(void) goto evnew_fail; } - g_cmdev = event_new(evbase_worker, g_cmd_pipe[0], EV_READ, command_cb, NULL); - if (!g_cmdev) - { - DPRINTF(E_LOG, L_MAIN, "Could not create cmd event\n"); - goto evnew_fail; - } + cmdbase = commands_base_new(evbase_worker); event_add(g_exitev, NULL); - event_add(g_cmdev, NULL); ret = pthread_create(&tid_worker, NULL, worker, NULL); if (ret < 0) @@ -352,15 +251,12 @@ worker_init(void) return 0; thread_fail: + commands_base_free(cmdbase); evnew_fail: event_base_free(evbase_worker); evbase_worker = NULL; evbase_fail: - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); - - cmd_fail: close(g_exit_pipe[0]); close(g_exit_pipe[1]); @@ -385,9 +281,8 @@ worker_deinit(void) // Free event base (should free events too) event_base_free(evbase_worker); - // Close pipes - close(g_cmd_pipe[0]); - close(g_cmd_pipe[1]); + // Close pipes and free command base + commands_base_free(cmdbase); close(g_exit_pipe[0]); close(g_exit_pipe[1]); }