Fix deadlock on exit if SKIP_FRAMES disabled

On exit, the renderer will not consume frames anymore, so signal the
condition variable to wake up the decoder.
This commit is contained in:
Romain Vimont 2018-02-09 08:42:39 +01:00
parent 629c296207
commit 127e56780a
5 changed files with 28 additions and 6 deletions

View file

@ -152,6 +152,10 @@ SDL_bool decoder_start(struct decoder *decoder) {
return SDL_TRUE; return SDL_TRUE;
} }
void decoder_stop(struct decoder *decoder) {
frames_stop(decoder->frames);
}
void decoder_join(struct decoder *decoder) { void decoder_join(struct decoder *decoder) {
SDL_WaitThread(decoder->thread, NULL); SDL_WaitThread(decoder->thread, NULL);
} }

View file

@ -14,6 +14,7 @@ struct decoder {
}; };
SDL_bool decoder_start(struct decoder *decoder); SDL_bool decoder_start(struct decoder *decoder);
void decoder_stop(struct decoder *decoder);
void decoder_join(struct decoder *decoder); void decoder_join(struct decoder *decoder);
#endif #endif

View file

@ -27,6 +27,7 @@ SDL_bool frames_init(struct frames *frames) {
SDL_DestroyMutex(frames->mutex); SDL_DestroyMutex(frames->mutex);
goto error_2; goto error_2;
} }
frames->stopped = SDL_FALSE;
#endif #endif
// there is initially no rendering frame, so consider it has already been // there is initially no rendering frame, so consider it has already been
@ -60,24 +61,23 @@ static void frames_swap(struct frames *frames) {
SDL_bool frames_offer_decoded_frame(struct frames *frames) { SDL_bool frames_offer_decoded_frame(struct frames *frames) {
mutex_lock(frames->mutex); mutex_lock(frames->mutex);
SDL_bool previous_frame_consumed;
#ifndef SKIP_FRAMES #ifndef SKIP_FRAMES
// if SKIP_FRAMES is disabled, then the decoder must wait for the current // if SKIP_FRAMES is disabled, then the decoder must wait for the current
// frame to be consumed // frame to be consumed
while (!frames->rendering_frame_consumed) { while (!frames->rendering_frame_consumed && !frames->stopped) {
cond_wait(frames->rendering_frame_consumed_cond, frames->mutex); cond_wait(frames->rendering_frame_consumed_cond, frames->mutex);
} }
// by definition, we are not skipping the frames
previous_frame_consumed = SDL_TRUE;
#else #else
previous_frame_consumed = frames->rendering_frame_consumed; if (!frames->rendering_frame_consumed) {
if (!previous_frame_consumed) {
SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Skip frame"); SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "Skip frame");
} }
#endif #endif
frames_swap(frames); frames_swap(frames);
SDL_bool previous_frame_consumed = frames->rendering_frame_consumed;
frames->rendering_frame_consumed = SDL_FALSE; frames->rendering_frame_consumed = SDL_FALSE;
mutex_unlock(frames->mutex); mutex_unlock(frames->mutex);
return previous_frame_consumed; return previous_frame_consumed;
} }
@ -92,3 +92,15 @@ const AVFrame *frames_consume_rendered_frame(struct frames *frames) {
#endif #endif
return frames->rendering_frame; return frames->rendering_frame;
} }
void frames_stop(struct frames *frames) {
#ifdef SKIP_FRAMES
(void) frames; // unused
#else
mutex_lock(frames->mutex);
frames->stopped = SDL_TRUE;
mutex_unlock(frames->mutex);
// wake up blocking wait
cond_signal(frames->rendering_frame_consumed_cond);
#endif
}

View file

@ -14,6 +14,7 @@ struct frames {
AVFrame *rendering_frame; AVFrame *rendering_frame;
SDL_mutex *mutex; SDL_mutex *mutex;
#ifndef SKIP_FRAMES #ifndef SKIP_FRAMES
SDL_bool stopped;
SDL_cond *rendering_frame_consumed_cond; SDL_cond *rendering_frame_consumed_cond;
#endif #endif
SDL_bool rendering_frame_consumed; SDL_bool rendering_frame_consumed;
@ -33,4 +34,7 @@ SDL_bool frames_offer_decoded_frame(struct frames *frames);
// unlocking frames->mutex // unlocking frames->mutex
const AVFrame *frames_consume_rendered_frame(struct frames *frames); const AVFrame *frames_consume_rendered_frame(struct frames *frames);
// wake up and avoid any blocking call
void frames_stop(struct frames *frames);
#endif #endif

View file

@ -180,6 +180,7 @@ finally_stop_and_join_controller:
finally_destroy_controller: finally_destroy_controller:
controller_destroy(&controller); controller_destroy(&controller);
finally_stop_decoder: finally_stop_decoder:
decoder_stop(&decoder);
// kill the server before decoder_join() to wake up the decoder // kill the server before decoder_join() to wake up the decoder
server_stop(&server, serial); server_stop(&server, serial);
decoder_join(&decoder); decoder_join(&decoder);