Commit 25a806a3 authored by Max Kellermann's avatar Max Kellermann

player_control: protect command, state, error with a mutex

Use GMutex/GCond instead of the notify library. Manually lock the player_control object before accessing the protected attributes. Use the GCond object to notify the player thread and the main thread.
parent 73cff374
...@@ -61,7 +61,7 @@ void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder, ...@@ -61,7 +61,7 @@ void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder,
dc.state = DECODE_STATE_DECODE; dc.state = DECODE_STATE_DECODE;
decoder_unlock(); decoder_unlock();
notify_signal(&pc.notify); player_lock_signal();
g_debug("audio_format=%u:%u:%u, seekable=%s", g_debug("audio_format=%u:%u:%u, seekable=%s",
dc.in_audio_format.sample_rate, dc.in_audio_format.bits, dc.in_audio_format.sample_rate, dc.in_audio_format.bits,
...@@ -112,7 +112,7 @@ void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder) ...@@ -112,7 +112,7 @@ void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder)
dc.command = DECODE_COMMAND_NONE; dc.command = DECODE_COMMAND_NONE;
decoder_unlock(); decoder_unlock();
notify_signal(&pc.notify); player_lock_signal();
} }
double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder) double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
...@@ -184,7 +184,7 @@ do_send_tag(struct decoder *decoder, struct input_stream *is, ...@@ -184,7 +184,7 @@ do_send_tag(struct decoder *decoder, struct input_stream *is,
/* there is a partial chunk - flush it, we want the /* there is a partial chunk - flush it, we want the
tag in a new chunk */ tag in a new chunk */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
notify_signal(&pc.notify); player_lock_signal();
} }
assert(decoder->chunk == NULL); assert(decoder->chunk == NULL);
...@@ -297,7 +297,7 @@ decoder_data(struct decoder *decoder, ...@@ -297,7 +297,7 @@ decoder_data(struct decoder *decoder,
if (dest == NULL) { if (dest == NULL) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
notify_signal(&pc.notify); player_lock_signal();
continue; continue;
} }
...@@ -324,7 +324,7 @@ decoder_data(struct decoder *decoder, ...@@ -324,7 +324,7 @@ decoder_data(struct decoder *decoder,
if (full) { if (full) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
notify_signal(&pc.notify); player_lock_signal();
} }
data += nbytes; data += nbytes;
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
*/ */
#include "decoder_control.h" #include "decoder_control.h"
#include "notify.h" #include "player_control.h"
#include <assert.h> #include <assert.h>
...@@ -40,38 +40,34 @@ void dc_deinit(void) ...@@ -40,38 +40,34 @@ void dc_deinit(void)
} }
static void static void
dc_command_wait_locked(struct notify *notify) dc_command_wait_locked(void)
{ {
while (dc.command != DECODE_COMMAND_NONE) { while (dc.command != DECODE_COMMAND_NONE) {
decoder_signal(); decoder_signal();
decoder_unlock(); player_wait_decoder();
notify_wait(notify);
decoder_lock();
} }
} }
void void
dc_command_wait(struct notify *notify) dc_command_wait(void)
{ {
decoder_lock(); decoder_lock();
dc_command_wait_locked(notify); dc_command_wait_locked();
decoder_unlock(); decoder_unlock();
} }
static void static void
dc_command_locked(struct notify *notify, enum decoder_command cmd) dc_command_locked(enum decoder_command cmd)
{ {
dc.command = cmd; dc.command = cmd;
dc_command_wait_locked(notify); dc_command_wait_locked();
} }
static void static void
dc_command(struct notify *notify, enum decoder_command cmd) dc_command(enum decoder_command cmd)
{ {
decoder_lock(); decoder_lock();
dc_command_locked(notify, cmd); dc_command_locked(cmd);
decoder_unlock(); decoder_unlock();
} }
...@@ -86,13 +82,13 @@ static void dc_command_async(enum decoder_command cmd) ...@@ -86,13 +82,13 @@ static void dc_command_async(enum decoder_command cmd)
} }
void void
dc_start(struct notify *notify, struct song *song) dc_start(struct song *song)
{ {
assert(dc.pipe != NULL); assert(dc.pipe != NULL);
assert(song != NULL); assert(song != NULL);
dc.next_song = song; dc.next_song = song;
dc_command(notify, DECODE_COMMAND_START); dc_command(DECODE_COMMAND_START);
} }
void void
...@@ -106,7 +102,7 @@ dc_start_async(struct song *song) ...@@ -106,7 +102,7 @@ dc_start_async(struct song *song)
} }
void void
dc_stop(struct notify *notify) dc_stop(void)
{ {
decoder_lock(); decoder_lock();
...@@ -115,16 +111,16 @@ dc_stop(struct notify *notify) ...@@ -115,16 +111,16 @@ dc_stop(struct notify *notify)
late and the decoder thread is already executing late and the decoder thread is already executing
the old command, we'll call STOP again in this the old command, we'll call STOP again in this
function (see below). */ function (see below). */
dc_command_locked(notify, DECODE_COMMAND_STOP); dc_command_locked(DECODE_COMMAND_STOP);
if (dc.state != DECODE_STATE_STOP && dc.state != DECODE_STATE_ERROR) if (dc.state != DECODE_STATE_STOP && dc.state != DECODE_STATE_ERROR)
dc_command_locked(notify, DECODE_COMMAND_STOP); dc_command_locked(DECODE_COMMAND_STOP);
decoder_unlock(); decoder_unlock();
} }
bool bool
dc_seek(struct notify *notify, double where) dc_seek(double where)
{ {
assert(dc.state != DECODE_STATE_START); assert(dc.state != DECODE_STATE_START);
assert(where >= 0.0); assert(where >= 0.0);
...@@ -135,7 +131,7 @@ dc_seek(struct notify *notify, double where) ...@@ -135,7 +131,7 @@ dc_seek(struct notify *notify, double where)
dc.seek_where = where; dc.seek_where = where;
dc.seek_error = false; dc.seek_error = false;
dc_command(notify, DECODE_COMMAND_SEEK); dc_command(DECODE_COMMAND_SEEK);
if (dc.seek_error) if (dc.seek_error)
return false; return false;
......
...@@ -30,8 +30,6 @@ ...@@ -30,8 +30,6 @@
#define DECODE_TYPE_FILE 0 #define DECODE_TYPE_FILE 0
#define DECODE_TYPE_URL 1 #define DECODE_TYPE_URL 1
struct notify;
enum decoder_state { enum decoder_state {
DECODE_STATE_STOP = 0, DECODE_STATE_STOP = 0,
DECODE_STATE_START, DECODE_STATE_START,
...@@ -205,19 +203,19 @@ decoder_current_song(void) ...@@ -205,19 +203,19 @@ decoder_current_song(void)
} }
void void
dc_command_wait(struct notify *notify); dc_command_wait(void);
void void
dc_start(struct notify *notify, struct song *song); dc_start(struct song *song);
void void
dc_start_async(struct song *song); dc_start_async(struct song *song);
void void
dc_stop(struct notify *notify); dc_stop(void);
bool bool
dc_seek(struct notify *notify, double where); dc_seek(double where);
void void
dc_quit(void); dc_quit(void);
......
...@@ -58,10 +58,7 @@ need_chunks(struct input_stream *is, bool do_wait) ...@@ -58,10 +58,7 @@ need_chunks(struct input_stream *is, bool do_wait)
if ((is == NULL || decoder_input_buffer(is) <= 0) && do_wait) { if ((is == NULL || decoder_input_buffer(is) <= 0) && do_wait) {
decoder_wait(); decoder_wait();
player_signal();
decoder_unlock();
notify_signal(&pc.notify);
decoder_lock();
return dc.command; return dc.command;
} }
......
...@@ -112,9 +112,7 @@ static void decoder_run_song(const struct song *song, const char *uri) ...@@ -112,9 +112,7 @@ static void decoder_run_song(const struct song *song, const char *uri)
dc.state = DECODE_STATE_START; dc.state = DECODE_STATE_START;
dc.command = DECODE_COMMAND_NONE; dc.command = DECODE_COMMAND_NONE;
decoder_unlock(); player_signal();
notify_signal(&pc.notify);
decoder_lock();
/* wait for the input stream to become ready; its metadata /* wait for the input stream to become ready; its metadata
will be available then */ will be available then */
...@@ -294,17 +292,13 @@ static gpointer decoder_task(G_GNUC_UNUSED gpointer arg) ...@@ -294,17 +292,13 @@ static gpointer decoder_task(G_GNUC_UNUSED gpointer arg)
dc.command = DECODE_COMMAND_NONE; dc.command = DECODE_COMMAND_NONE;
decoder_unlock(); player_signal();
notify_signal(&pc.notify);
decoder_lock();
break; break;
case DECODE_COMMAND_STOP: case DECODE_COMMAND_STOP:
dc.command = DECODE_COMMAND_NONE; dc.command = DECODE_COMMAND_NONE;
decoder_unlock(); player_signal();
notify_signal(&pc.notify);
decoder_lock();
break; break;
case DECODE_COMMAND_NONE: case DECODE_COMMAND_NONE:
......
...@@ -93,7 +93,7 @@ enum { ...@@ -93,7 +93,7 @@ enum {
GThread *main_task; GThread *main_task;
GMainLoop *main_loop; GMainLoop *main_loop;
struct notify main_notify; GCond *main_cond;
static void static void
glue_daemonize_init(const struct options *options) glue_daemonize_init(const struct options *options)
...@@ -321,7 +321,7 @@ int main(int argc, char *argv[]) ...@@ -321,7 +321,7 @@ int main(int argc, char *argv[])
main_task = g_thread_self(); main_task = g_thread_self();
main_loop = g_main_loop_new(NULL, FALSE); main_loop = g_main_loop_new(NULL, FALSE);
notify_init(&main_notify); main_cond = g_cond_new();
event_pipe_init(); event_pipe_init();
event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted); event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
...@@ -415,7 +415,7 @@ int main(int argc, char *argv[]) ...@@ -415,7 +415,7 @@ int main(int argc, char *argv[])
sticker_global_finish(); sticker_global_finish();
#endif #endif
notify_deinit(&main_notify); g_cond_free(main_cond);
event_pipe_deinit(); event_pipe_deinit();
playlist_list_global_finish(); playlist_list_global_finish();
......
...@@ -26,6 +26,6 @@ extern GThread *main_task; ...@@ -26,6 +26,6 @@ extern GThread *main_task;
extern GMainLoop *main_loop; extern GMainLoop *main_loop;
extern struct notify main_notify; extern GCond *main_cond;
#endif #endif
...@@ -445,10 +445,15 @@ audio_output_all_check(void) ...@@ -445,10 +445,15 @@ audio_output_all_check(void)
bool bool
audio_output_all_wait(unsigned threshold) audio_output_all_wait(unsigned threshold)
{ {
if (audio_output_all_check() < threshold) player_lock();
if (audio_output_all_check() < threshold) {
player_unlock();
return true; return true;
}
notify_wait(&pc.notify); player_wait();
player_unlock();
return audio_output_all_check() < threshold; return audio_output_all_check() < threshold;
} }
......
...@@ -359,7 +359,7 @@ ao_play(struct audio_output *ao) ...@@ -359,7 +359,7 @@ ao_play(struct audio_output *ao)
ao->chunk_finished = true; ao->chunk_finished = true;
g_mutex_unlock(ao->mutex); g_mutex_unlock(ao->mutex);
notify_signal(&pc.notify); player_lock_signal();
g_mutex_lock(ao->mutex); g_mutex_lock(ao->mutex);
return true; return true;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
*/ */
#include "player_control.h" #include "player_control.h"
#include "decoder_control.h"
#include "path.h" #include "path.h"
#include "log.h" #include "log.h"
#include "tag.h" #include "tag.h"
...@@ -35,7 +36,10 @@ void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play) ...@@ -35,7 +36,10 @@ void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play)
{ {
pc.buffer_chunks = buffer_chunks; pc.buffer_chunks = buffer_chunks;
pc.buffered_before_play = buffered_before_play; pc.buffered_before_play = buffered_before_play;
notify_init(&pc.notify);
pc.mutex = g_mutex_new();
pc.cond = g_cond_new();
pc.command = PLAYER_COMMAND_NONE; pc.command = PLAYER_COMMAND_NONE;
pc.error = PLAYER_ERROR_NOERROR; pc.error = PLAYER_ERROR_NOERROR;
pc.state = PLAYER_STATE_STOP; pc.state = PLAYER_STATE_STOP;
...@@ -44,7 +48,16 @@ void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play) ...@@ -44,7 +48,16 @@ void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play)
void pc_deinit(void) void pc_deinit(void)
{ {
notify_deinit(&pc.notify); g_cond_free(pc.cond);
g_mutex_free(pc.mutex);
}
void
player_wait_decoder(void)
{
/* during this function, the decoder lock is held, because
we're waiting for the decoder thread */
g_cond_wait(pc.cond, dc.mutex);
} }
void void
...@@ -56,15 +69,30 @@ pc_song_deleted(const struct song *song) ...@@ -56,15 +69,30 @@ pc_song_deleted(const struct song *song)
} }
} }
static void player_command(enum player_command cmd) static void
player_command_wait_locked(void)
{
while (pc.command != PLAYER_COMMAND_NONE) {
player_signal();
g_cond_wait(main_cond, pc.mutex);
}
}
static void
player_command_locked(enum player_command cmd)
{ {
assert(pc.command == PLAYER_COMMAND_NONE); assert(pc.command == PLAYER_COMMAND_NONE);
pc.command = cmd; pc.command = cmd;
while (pc.command != PLAYER_COMMAND_NONE) { player_command_wait_locked();
notify_signal(&pc.notify); }
notify_wait(&main_notify);
} static void
player_command(enum player_command cmd)
{
player_lock();
player_command_locked(cmd);
player_unlock();
} }
void void
......
...@@ -88,10 +88,19 @@ struct player_control { ...@@ -88,10 +88,19 @@ struct player_control {
thread isn't running */ thread isn't running */
GThread *thread; GThread *thread;
struct notify notify; /**
volatile enum player_command command; * This lock protects #command, #state, #error.
volatile enum player_state state; */
volatile enum player_error error; GMutex *mutex;
/**
* Trigger this object after you have modified #command.
*/
GCond *cond;
enum player_command command;
enum player_state state;
enum player_error error;
uint16_t bit_rate; uint16_t bit_rate;
struct audio_format audio_format; struct audio_format audio_format;
float total_time; float total_time;
...@@ -110,6 +119,67 @@ void pc_init(unsigned buffer_chunks, unsigned buffered_before_play); ...@@ -110,6 +119,67 @@ void pc_init(unsigned buffer_chunks, unsigned buffered_before_play);
void pc_deinit(void); void pc_deinit(void);
/** /**
* Locks the #player_control object.
*/
static inline void
player_lock(void)
{
g_mutex_lock(pc.mutex);
}
/**
* Unlocks the #player_control object.
*/
static inline void
player_unlock(void)
{
g_mutex_unlock(pc.mutex);
}
/**
* Waits for a signal on the #player_control object. This function is
* only valid in the player thread. The object must be locked prior
* to calling this function.
*/
static inline void
player_wait(void)
{
g_cond_wait(pc.cond, pc.mutex);
}
/**
* Waits for a signal on the #player_control object. This function is
* only valid in the player thread. The #decoder_control object must
* be locked prior to calling this function.
*
* Note the small difference to the player_wait() function!
*/
void
player_wait_decoder(void);
/**
* Signals the #player_control object. The object should be locked
* prior to calling this function.
*/
static inline void
player_signal(void)
{
g_cond_signal(pc.cond);
}
/**
* Signals the #player_control object. The object is temporarily
* locked by this function.
*/
static inline void
player_lock_signal(void)
{
player_lock();
player_signal();
player_unlock();
}
/**
* Call this function when the specified song pointer is about to be * Call this function when the specified song pointer is about to be
* invalidated. This makes sure that player_control.errored_song does * invalidated. This makes sure that player_control.errored_song does
* not point to an invalid pointer. * not point to an invalid pointer.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment