[library] Add action to library_schedule_callback()

Makes it possible for the caller to request that a previous callback is
replaced, so we don't end up with more and more callbacks. Also add other
options for future use.
This commit is contained in:
ejurgensen 2020-03-23 23:17:26 +01:00
parent 2af7296723
commit 2d8521139c
3 changed files with 71 additions and 21 deletions

View File

@ -54,6 +54,7 @@ struct library_callback_register
{ {
library_cb cb; library_cb cb;
void *arg; void *arg;
struct event *ev;
}; };
struct playlist_item_add_param struct playlist_item_add_param
@ -170,44 +171,71 @@ static void
scheduled_cb(int fd, short what, void *arg) scheduled_cb(int fd, short what, void *arg)
{ {
struct library_callback_register *cbreg = 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); // Must reset the register before calling back, otherwise it won't work if the
// callback reschedules by calling library_callback_schedule()
cbreg->cb(cbreg->arg); event_free(cbreg->ev);
memset(cbreg, 0, sizeof(struct library_callback_register)); memset(cbreg, 0, sizeof(struct library_callback_register));
DPRINTF(E_DBG, L_LIB, "Executing library callback to %p\n", cb);
cb(cb_arg);
} }
int 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; struct library_callback_register *cbreg;
int callback_id; bool replace_done;
int idx_available;
int i;
// Find a free slot in the queue for (i = 0, idx_available = -1, replace_done = false; i < ARRAY_SIZE(library_cb_register); i++)
for (callback_id = 0; callback_id < ARRAY_SIZE(library_cb_register); callback_id++)
{ {
if (library_cb_register[callback_id].cb == NULL) if (idx_available == -1 && library_cb_register[i].cb == NULL)
break; 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; return -1;
} }
cbreg = &library_cb_register[callback_id]; cbreg = &library_cb_register[idx_available];
cbreg->cb = cb; cbreg->cb = cb;
cbreg->arg = arg; cbreg->arg = arg;
// One-time event, freed automatically by libevent if (!cbreg->ev)
CHECK_ERR(L_LIB, event_base_once(evbase_lib, -1, EV_TIMEOUT, scheduled_cb, cbreg, wait)); 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(); 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_free(updateev);
event_base_free(evbase_lib); event_base_free(evbase_lib);
} }

View File

@ -32,6 +32,21 @@
typedef void (*library_cb)(void *arg); 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 * Definition of a library source
* *
@ -123,10 +138,11 @@ library_playlist_save(struct playlist_info *pli);
* @param cb Callback to call * @param cb Callback to call
* @param arg Argument to call back with * @param arg Argument to call back with
* @param timeval How long to wait before calling back * @param timeval How long to wait before calling back
* @param action (see enum)
* @return id of the scheduled event, -1 on failure * @return id of the scheduled event, -1 on failure
*/ */
int 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 * @return true if a running scan should be aborted due to imminent shutdown

View File

@ -549,7 +549,7 @@ rss_scan_all(enum rss_scan_type scan_type)
if (count == 0) if (count == 0)
return; 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); 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) if (ret < 0)
return LIBRARY_PATH_INVALID; 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; return LIBRARY_OK;
} }