[player] Consolidate listener handling in one player trigger - wip

The goal is to make the listener invokation more unified and less ad hoc. Also
reduce risk of blocking/deadlocking player thread.
This commit is contained in:
ejurgensen 2020-05-24 20:27:46 +02:00
parent 3885b92111
commit 3b033e48ee
2 changed files with 100 additions and 46 deletions

View File

@ -772,8 +772,6 @@ outputs_device_add(struct output_device *add, bool new_deselect)
device->advertised = 1; device->advertised = 1;
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
return device; return device;
} }
@ -817,8 +815,6 @@ outputs_device_remove(struct output_device *remove)
outputs_device_free(remove); outputs_device_free(remove);
vol_adjust(); vol_adjust();
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
} }
void void
@ -915,8 +911,6 @@ outputs_device_volume_register(struct output_device *device, int absvol, int rel
device->volume = rel_to_vol(relvol, outputs_master_volume); device->volume = rel_to_vol(relvol, outputs_master_volume);
vol_adjust(); vol_adjust();
listener_notify(LISTENER_VOLUME);
} }
int int
@ -1099,8 +1093,6 @@ outputs_volume_set(int volume, output_status_cb cb)
pending += ret; pending += ret;
} }
listener_notify(LISTENER_VOLUME);
return pending; return pending;
} }

View File

@ -23,7 +23,7 @@
* - handle playback commands, status checks and events from other threads * - handle playback commands, status checks and events from other threads
* - receive audio from the input thread and to own the playback buffer * - receive audio from the input thread and to own the playback buffer
* - feed the outputs at the appropriate rate (controlled by the playback timer) * - feed the outputs at the appropriate rate (controlled by the playback timer)
* - output device handling (partly outsourced to outputs.c) * - output device handling (outsourced to outputs.c)
* - notify about playback status changes * - notify about playback status changes
* - maintain the playback queue * - maintain the playback queue
* *
@ -33,6 +33,15 @@
* not always obeyed, for instance some outputs do their setup in ways that * not always obeyed, for instance some outputs do their setup in ways that
* could block. * could block.
* *
* Listener events
* ---------------
* Events will be signaled via listener_notify(). The following rules apply to
* how this must be done in the code:
* - always use trigger_listener_notify() to make sure the callbacks that the listener
* makes do not block the player thread, and to avoid any risk of deadlocks
* - if the event is a result of an external command then trigger it when the
* command is completed, so generally after commands_exec_sync()
*
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -367,6 +376,27 @@ scrobble_cb(void *arg)
} }
#endif #endif
// Callback from the worker thread (async operation as it may block)
static void
trigger_listener_notify_cb(void *arg)
{
short *events = arg;
DPRINTF(E_DBG, L_PLAYER, "Executing listener notification %d\n", *events);
listener_notify(*events);
}
#define trigger_listener_notify(x) trigger_listener((x), __func__)
static void
trigger_listener(short events, const char *who)
{
DPRINTF(E_DBG, L_PLAYER, "Scheduling listener notification %d from %s\n", events, who);
worker_execute(trigger_listener_notify_cb, &events, sizeof(events), 0);
}
/* /*
* Add the song with the given id to the list of previously played songs * Add the song with the given id to the list of previously played songs
*/ */
@ -461,7 +491,7 @@ status_update(enum play_status status)
{ {
player_state = status; player_state = status;
listener_notify(LISTENER_PLAYER); trigger_listener_notify(LISTENER_PLAYER);
} }
/* ------ All this is for dealing with metadata received from the input ----- */ /* ------ All this is for dealing with metadata received from the input ----- */
@ -1348,8 +1378,15 @@ device_add(void *arg, int *retval)
new_deselect = (player_state == PLAY_PLAYING); new_deselect = (player_state == PLAY_PLAYING);
device = outputs_device_add(device, new_deselect); device = outputs_device_add(device, new_deselect);
*retval = device ? 0 : -1; if (!device)
{
*retval = -1;
return COMMAND_END;
}
trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
*retval = 0;
return COMMAND_END; return COMMAND_END;
} }
@ -1401,6 +1438,8 @@ device_remove_family(void *arg, int *retval)
outputs_device_free(remove); outputs_device_free(remove);
trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
*retval = 0; *retval = 0;
return COMMAND_END; return COMMAND_END;
} }
@ -1425,12 +1464,8 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu
if (!device) if (!device)
{ {
DPRINTF(E_LOG, L_PLAYER, "Output device disappeared during streaming!\n"); DPRINTF(E_LOG, L_PLAYER, "Output device disappeared during streaming!\n");
return;
} }
else if (status == OUTPUT_STATE_FAILED)
DPRINTF(E_DBG, L_PLAYER, "Callback from %s device %s to device_streaming_cb (status %d)\n", device->type_name, device->name, status);
if (status == OUTPUT_STATE_FAILED)
{ {
DPRINTF(E_LOG, L_PLAYER, "The %s device '%s' failed - attempting reconnect in %d sec\n", device->type_name, device->name, PLAYER_SPEAKER_RESURRECT_TIME); DPRINTF(E_LOG, L_PLAYER, "The %s device '%s' failed - attempting reconnect in %d sec\n", device->type_name, device->name, PLAYER_SPEAKER_RESURRECT_TIME);
@ -1445,11 +1480,19 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu
DPRINTF(E_INFO, L_PLAYER, "The %s device '%s' stopped\n", device->type_name, device->name); DPRINTF(E_INFO, L_PLAYER, "The %s device '%s' stopped\n", device->type_name, device->name);
} }
else else
{
DPRINTF(E_DBG, L_PLAYER, "Callback from %s device %s to device_streaming_cb (status %d)\n", device->type_name, device->name, status);
outputs_device_cb_set(device, device_streaming_cb); outputs_device_cb_set(device, device_streaming_cb);
}
// We don't do this in the other cb's because they are triggered by a command
// and thus the caller is responsible for making the notification when the
// command is completed
trigger_listener_notify(LISTENER_SPEAKER);
} }
static void static void
device_command_cb(struct output_device *device, enum output_device_state status) device_volume_cb(struct output_device *device, enum output_device_state status)
{ {
if (!device) if (!device)
{ {
@ -1457,7 +1500,7 @@ device_command_cb(struct output_device *device, enum output_device_state status)
goto out; goto out;
} }
DPRINTF(E_DBG, L_PLAYER, "Callback from %s device %s to device_command_cb (status %d)\n", device->type_name, device->name, status); DPRINTF(E_DBG, L_PLAYER, "Callback from %s device %s to device_volume_cb (status %d)\n", device->type_name, device->name, status);
outputs_device_cb_set(device, device_streaming_cb); outputs_device_cb_set(device, device_streaming_cb);
@ -1565,8 +1608,8 @@ player_pmap(void *p)
return "device_activate_cb"; return "device_activate_cb";
else if (p == device_streaming_cb) else if (p == device_streaming_cb)
return "device_streaming_cb"; return "device_streaming_cb";
else if (p == device_command_cb) else if (p == device_volume_cb)
return "device_command_cb"; return "device_volume_cb";
else if (p == device_flush_cb) else if (p == device_flush_cb)
return "device_flush_cb"; return "device_flush_cb";
else if (p == device_shutdown_cb) else if (p == device_shutdown_cb)
@ -2720,7 +2763,7 @@ volume_set(void *arg, int *retval)
volume = cmdarg->intval; volume = cmdarg->intval;
*retval = outputs_volume_set(volume, device_command_cb); *retval = outputs_volume_set(volume, device_volume_cb);
if (*retval > 0) if (*retval > 0)
return COMMAND_PENDING; // async return COMMAND_PENDING; // async
@ -2744,7 +2787,7 @@ volume_setrel_speaker(void *arg, int *retval)
outputs_device_volume_register(device, -1, vol_param->volume); outputs_device_volume_register(device, -1, vol_param->volume);
*retval = outputs_device_volume_set(device, device_command_cb); *retval = outputs_device_volume_set(device, device_volume_cb);
if (*retval > 0) if (*retval > 0)
return COMMAND_PENDING; // async return COMMAND_PENDING; // async
@ -2768,7 +2811,7 @@ volume_setabs_speaker(void *arg, int *retval)
outputs_device_volume_register(device, vol_param->volume, -1); outputs_device_volume_register(device, vol_param->volume, -1);
*retval = outputs_device_volume_set(device, device_command_cb); *retval = outputs_device_volume_set(device, device_volume_cb);
if (*retval > 0) if (*retval > 0)
return COMMAND_PENDING; // async return COMMAND_PENDING; // async
@ -2831,8 +2874,6 @@ repeat_set(void *arg, int *retval)
return COMMAND_END; return COMMAND_END;
} }
listener_notify(LISTENER_OPTIONS);
*retval = 0; *retval = 0;
return COMMAND_END; return COMMAND_END;
} }
@ -2862,9 +2903,8 @@ shuffle_set(void *arg, int *retval)
db_queue_inc_version(); db_queue_inc_version();
} }
// Update shuffle mode and notify listeners // Update shuffle mode
shuffle = new_shuffle; shuffle = new_shuffle;
listener_notify(LISTENER_OPTIONS);
out: out:
*retval = 0; *retval = 0;
@ -2878,8 +2918,6 @@ consume_set(void *arg, int *retval)
consume = cmdarg->intval; consume = cmdarg->intval;
listener_notify(LISTENER_OPTIONS);
*retval = 0; *retval = 0;
return COMMAND_END; return COMMAND_END;
} }
@ -2894,8 +2932,6 @@ playerqueue_clear_history(void *arg, int *retval)
cur_plversion++; // TODO [db_queue] need to update db queue version cur_plversion++; // TODO [db_queue] need to update db queue version
listener_notify(LISTENER_QUEUE);
*retval = 0; *retval = 0;
return COMMAND_END; return COMMAND_END;
} }
@ -3083,8 +3119,8 @@ player_speaker_set(uint64_t *ids)
speaker_set_param.device_ids = ids; speaker_set_param.device_ids = ids;
ret = commands_exec_sync(cmdbase, speaker_set, NULL, &speaker_set_param); ret = commands_exec_sync(cmdbase, speaker_set, NULL, &speaker_set_param);
if (ret >= 0)
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME); trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
return ret; return ret;
} }
@ -3121,8 +3157,8 @@ player_speaker_enable(uint64_t id)
int ret; int ret;
ret = commands_exec_sync(cmdbase, speaker_enable, NULL, &id); ret = commands_exec_sync(cmdbase, speaker_enable, NULL, &id);
if (ret >= 0)
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME); trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
return ret; return ret;
} }
@ -3133,8 +3169,8 @@ player_speaker_disable(uint64_t id)
int ret; int ret;
ret = commands_exec_sync(cmdbase, speaker_disable, NULL, &id); ret = commands_exec_sync(cmdbase, speaker_disable, NULL, &id);
if (ret >= 0)
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME); trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
return ret; return ret;
} }
@ -3149,8 +3185,8 @@ player_speaker_prevent_playback_set(uint64_t id, bool prevent_playback)
param.prevent_playback = prevent_playback; param.prevent_playback = prevent_playback;
ret = commands_exec_sync(cmdbase, speaker_prevent_playback_set, speaker_prevent_playback_set_bh, &param); ret = commands_exec_sync(cmdbase, speaker_prevent_playback_set, speaker_prevent_playback_set_bh, &param);
if (ret >= 0)
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME); trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
return ret; return ret;
} }
@ -3165,8 +3201,8 @@ player_speaker_busy_set(uint64_t id, bool busy)
param.busy = busy; param.busy = busy;
ret = commands_exec_sync(cmdbase, speaker_busy_set, speaker_prevent_playback_set_bh, &param); ret = commands_exec_sync(cmdbase, speaker_busy_set, speaker_prevent_playback_set_bh, &param);
if (ret >= 0)
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME); trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
return ret; return ret;
} }
@ -3175,12 +3211,13 @@ void
player_speaker_resurrect(void *arg) player_speaker_resurrect(void *arg)
{ {
struct speaker_set_param param; struct speaker_set_param param;
int ret;
param.device_ids = (uint64_t *)arg; param.device_ids = (uint64_t *)arg;
commands_exec_sync(cmdbase, speaker_resurrect, speaker_resurrect_bh, &param); ret = commands_exec_sync(cmdbase, speaker_resurrect, speaker_resurrect_bh, &param);
if (ret >= 0)
listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME); trigger_listener_notify(LISTENER_SPEAKER | LISTENER_VOLUME);
} }
int int
@ -3198,6 +3235,9 @@ player_volume_set(int vol)
cmdarg.intval = vol; cmdarg.intval = vol;
ret = commands_exec_sync(cmdbase, volume_set, NULL, &cmdarg); ret = commands_exec_sync(cmdbase, volume_set, NULL, &cmdarg);
if (ret >= 0)
trigger_listener_notify(LISTENER_VOLUME);
return ret; return ret;
} }
@ -3217,6 +3257,9 @@ player_volume_setrel_speaker(uint64_t id, int relvol)
vol_param.volume = relvol; vol_param.volume = relvol;
ret = commands_exec_sync(cmdbase, volume_setrel_speaker, NULL, &vol_param); ret = commands_exec_sync(cmdbase, volume_setrel_speaker, NULL, &vol_param);
if (ret >= 0)
trigger_listener_notify(LISTENER_VOLUME);
return ret; return ret;
} }
@ -3236,6 +3279,9 @@ player_volume_setabs_speaker(uint64_t id, int vol)
vol_param.volume = vol; vol_param.volume = vol;
ret = commands_exec_sync(cmdbase, volume_setabs_speaker, NULL, &vol_param); ret = commands_exec_sync(cmdbase, volume_setabs_speaker, NULL, &vol_param);
if (ret >= 0)
trigger_listener_notify(LISTENER_VOLUME);
return ret; return ret;
} }
@ -3249,6 +3295,9 @@ player_volume_update_speaker(uint64_t id, const char *volstr)
vol_param.volstr = volstr; vol_param.volstr = volstr;
ret = commands_exec_sync(cmdbase, volume_update_speaker, NULL, &vol_param); ret = commands_exec_sync(cmdbase, volume_update_speaker, NULL, &vol_param);
if (ret >= 0)
trigger_listener_notify(LISTENER_VOLUME);
return ret; return ret;
} }
@ -3258,6 +3307,9 @@ player_repeat_set(enum repeat_mode mode)
int ret; int ret;
ret = commands_exec_sync(cmdbase, repeat_set, NULL, &mode); ret = commands_exec_sync(cmdbase, repeat_set, NULL, &mode);
if (ret >= 0)
trigger_listener_notify(LISTENER_OPTIONS);
return ret; return ret;
} }
@ -3270,6 +3322,9 @@ player_shuffle_set(int enable)
cmdarg.intval = enable; cmdarg.intval = enable;
ret = commands_exec_sync(cmdbase, shuffle_set, NULL, &cmdarg); ret = commands_exec_sync(cmdbase, shuffle_set, NULL, &cmdarg);
if (ret >= 0)
trigger_listener_notify(LISTENER_OPTIONS);
return ret; return ret;
} }
@ -3282,13 +3337,20 @@ player_consume_set(int enable)
cmdarg.intval = enable; cmdarg.intval = enable;
ret = commands_exec_sync(cmdbase, consume_set, NULL, &cmdarg); ret = commands_exec_sync(cmdbase, consume_set, NULL, &cmdarg);
if (ret >= 0)
trigger_listener_notify(LISTENER_OPTIONS);
return ret; return ret;
} }
void void
player_queue_clear_history() player_queue_clear_history()
{ {
commands_exec_sync(cmdbase, playerqueue_clear_history, NULL, NULL); int ret;
ret = commands_exec_sync(cmdbase, playerqueue_clear_history, NULL, NULL);
if (ret >= 0)
trigger_listener_notify(LISTENER_QUEUE);
} }
void void