diff --git a/src/library.c b/src/library.c index ecdd79f6..98d37f7d 100644 --- a/src/library.c +++ b/src/library.c @@ -54,6 +54,7 @@ struct library_callback_register { library_cb cb; void *arg; + struct event *ev; }; struct playlist_item_add_param @@ -170,44 +171,71 @@ static void scheduled_cb(int fd, short what, void *arg) { struct library_callback_register *cbreg = arg; + library_cb cb = cbreg->cb; + void *cb_arg = cbreg->arg; - DPRINTF(E_DBG, L_LIB, "Executing library callback to %p\n", cbreg->cb); - - cbreg->cb(cbreg->arg); - + // Must reset the register before calling back, otherwise it won't work if the + // callback reschedules by calling library_callback_schedule() + event_free(cbreg->ev); memset(cbreg, 0, sizeof(struct library_callback_register)); + + DPRINTF(E_DBG, L_LIB, "Executing library callback to %p\n", cb); + cb(cb_arg); } int -library_callback_schedule(library_cb cb, void *arg, struct timeval *wait) +library_callback_schedule(library_cb cb, void *arg, struct timeval *wait, enum library_cb_action action) { struct library_callback_register *cbreg; - int callback_id; + bool replace_done; + int idx_available; + int i; - // Find a free slot in the queue - for (callback_id = 0; callback_id < ARRAY_SIZE(library_cb_register); callback_id++) + for (i = 0, idx_available = -1, replace_done = false; i < ARRAY_SIZE(library_cb_register); i++) { - if (library_cb_register[callback_id].cb == NULL) - break; + if (idx_available == -1 && library_cb_register[i].cb == NULL) + idx_available = i; + + if (library_cb_register[i].cb != cb) + continue; + + if (action == LIBRARY_CB_REPLACE || action == LIBRARY_CB_ADD_OR_REPLACE) + { + event_add(library_cb_register[i].ev, wait); + library_cb_register[i].arg = arg; + replace_done = true; + } + else if (action == LIBRARY_CB_DELETE) + { + event_free(library_cb_register[i].ev); + memset(&library_cb_register[i], 0, sizeof(struct library_callback_register)); + } } - if (callback_id == ARRAY_SIZE(library_cb_register)) + if (action == LIBRARY_CB_REPLACE || action == LIBRARY_CB_DELETE || (action == LIBRARY_CB_ADD_OR_REPLACE && replace_done)) { - DPRINTF(E_LOG, L_LIB, "Library callback register is full! (size is %d)\n", LIBRARY_MAX_CALLBACKS); + return 0; // All done + } + else if (idx_available == -1) + { + DPRINTF(E_LOG, L_LIB, "Error scheduling callback, register full (size=%d, action=%d)\n", LIBRARY_MAX_CALLBACKS, action); return -1; } - cbreg = &library_cb_register[callback_id]; - + cbreg = &library_cb_register[idx_available]; cbreg->cb = cb; cbreg->arg = arg; - // One-time event, freed automatically by libevent - CHECK_ERR(L_LIB, event_base_once(evbase_lib, -1, EV_TIMEOUT, scheduled_cb, cbreg, wait)); + if (!cbreg->ev) + cbreg->ev = evtimer_new(evbase_lib, scheduled_cb, cbreg); - DPRINTF(E_DBG, L_LIB, "Added library callback to %p (id %d)\n", cbreg->cb, callback_id); + CHECK_NULL(L_LIB, cbreg->ev); - return callback_id; + event_add(cbreg->ev, wait); + + DPRINTF(E_DBG, L_LIB, "Added library callback to %p (id %d), wait %ld.%06ld\n", cbreg->cb, idx_available, wait->tv_sec, wait->tv_usec); + + return idx_available; } @@ -853,6 +881,12 @@ library_deinit() sources[i]->deinit(); } + for (i = 0; i < ARRAY_SIZE(library_cb_register); i++) + { + if (library_cb_register[i].ev) + event_free(library_cb_register[i].ev); + } + event_free(updateev); event_base_free(evbase_lib); } diff --git a/src/library.h b/src/library.h index 878ce010..0c0a661a 100644 --- a/src/library.h +++ b/src/library.h @@ -32,6 +32,21 @@ typedef void (*library_cb)(void *arg); +/* + * Argument to library_callback_schedule() + */ +enum library_cb_action +{ + // Add as new callback + LIBRARY_CB_ADD, + // Replace callback if it already exists + LIBRARY_CB_REPLACE, + // Replace callback if it already exists, otherwise add as new + LIBRARY_CB_ADD_OR_REPLACE, + // Delete a callback + LIBRARY_CB_DELETE, +}; + /* * Definition of a library source * @@ -123,10 +138,11 @@ library_playlist_save(struct playlist_info *pli); * @param cb Callback to call * @param arg Argument to call back with * @param timeval How long to wait before calling back + * @param action (see enum) * @return id of the scheduled event, -1 on failure */ int -library_callback_schedule(library_cb cb, void *arg, struct timeval *wait); +library_callback_schedule(library_cb cb, void *arg, struct timeval *wait, enum library_cb_action action); /* * @return true if a running scan should be aborted due to imminent shutdown diff --git a/src/library/rssscanner.c b/src/library/rssscanner.c index b59866ff..afb9db32 100644 --- a/src/library/rssscanner.c +++ b/src/library/rssscanner.c @@ -549,7 +549,7 @@ rss_scan_all(enum rss_scan_type scan_type) if (count == 0) return; - library_callback_schedule(rss_refresh, NULL, &rss_refresh_interval); + library_callback_schedule(rss_refresh, NULL, &rss_refresh_interval, LIBRARY_CB_ADD_OR_REPLACE); DPRINTF(E_INFO, L_LIB, "Refreshed %d RSS feeds in %.f sec (scan type %d)\n", count, difftime(end, start), scan_type); } @@ -601,7 +601,7 @@ rss_add(const char *path) if (ret < 0) return LIBRARY_PATH_INVALID; - library_callback_schedule(rss_refresh, NULL, &rss_refresh_interval); + library_callback_schedule(rss_refresh, NULL, &rss_refresh_interval, LIBRARY_CB_ADD_OR_REPLACE); return LIBRARY_OK; }