[vlc-devel] [PATCH] Avoid deadlock on stop() using UDP on Android
Romain Vimont | ®om
rom at rom1v.com
Sat May 10 08:57:34 CEST 2014
My explanation about pthread_cancel() is inexact: vlc_cancel() is called
in udp.c/Close(), so it is causally dependent of the cond_wait()
wake up. Therefore, this is wrong to say that vlc_cancel() should wake
up cond_wait().
The lack of pthread_cancel() only prevent to fix the problem calling
vlc_cancel() earlier. So there is no reason the deadlock can not be
reproduced on other platforms, that this patch should fix.
Le vendredi 9 mai 2014 à 17:58 +0200, Romain Vimont a écrit :
> Android has no pthread_cancel(). Its behavior is emulated in
> vlc_cancel() using a flag "killed", but it can lead to unavoidable race
> conditions, typically between a call to vlc_object_alive() and a
> blocking cond_wait().
>
> This leads to reproducible (with an acceptable probability) deadlocks
> when stopping just after playing UDP streams on Android:
> https://mailman.videolan.org/pipermail/vlc-devel/2014-April/097861.html
> https://mailman.videolan.org/pipermail/vlc-devel/2014-April/097912.html
>
> Thus, we need to be able to wake up cond_wait() once the thread has to
> finish. The commit 57eee64 did it, but it fails when block_FifoWake() is
> called while the queue is not empty (in that case, it has no effect).
> This is probably not a problem on platforms where pthread_cancel() is
> available (the cond_wait() would be awakened), but this is a problem on
> Android.
>
> It seems the "b_force_wake" flag semantic could not be modified to match
> a "finishing" state (e.g. block_FifoWake() is called in
> input_DecoderWait()). Therefore, a separate flag "b_finishing" is added,
> and set at the end of the UDP ThreadRead(), leading to set the "b_eof"
> flag in the access_t.info structure.
>
> With these changes, I cannot reproduce the deadlock at all.
>
> Signed-off-by: Romain Vimont <rom at rom1v.com>
> ---
> include/vlc_block.h | 2 ++
> modules/access/udp.c | 10 ++++++++--
> src/misc/block.c | 39 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 49 insertions(+), 2 deletions(-)
>
> diff --git a/include/vlc_block.h b/include/vlc_block.h
> index 75f98ff..264fd95 100644
> --- a/include/vlc_block.h
> +++ b/include/vlc_block.h
> @@ -313,6 +313,8 @@ VLC_API void block_FifoPace( block_fifo_t *fifo, size_t max_depth, size_t max_si
> VLC_API void block_FifoEmpty( block_fifo_t * );
> VLC_API size_t block_FifoPut( block_fifo_t *, block_t * );
> VLC_API void block_FifoWake( block_fifo_t * );
> +VLC_API void block_FifoFinish( block_fifo_t * );
> +VLC_API bool block_FifoIsFinishing( block_fifo_t * );
> VLC_API block_t * block_FifoGet( block_fifo_t * ) VLC_USED;
> VLC_API block_t * block_FifoShow( block_fifo_t * );
> size_t block_FifoSize( const block_fifo_t *p_fifo ) VLC_USED;
> diff --git a/modules/access/udp.c b/modules/access/udp.c
> index 3c18cb8..29d5c53 100644
> --- a/modules/access/udp.c
> +++ b/modules/access/udp.c
> @@ -231,7 +231,13 @@ static block_t *BlockUDP( access_t *p_access )
> {
> access_sys_t *sys = p_access->p_sys;
>
> - return block_FifoGet( sys->fifo );
> + block_t *block = block_FifoGet( sys->fifo );
> + if( unlikely( block_FifoIsFinishing( sys->fifo ) ) )
> + {
> + /* We should not read data anymore */
> + p_access->info.b_eof = true;
> + }
> + return block;
> }
>
> /*****************************************************************************
> @@ -270,6 +276,6 @@ static void* ThreadRead( void *data )
> block_FifoPut( sys->fifo, pkt );
> }
>
> - block_FifoWake( sys->fifo );
> + block_FifoFinish( sys->fifo );
> return NULL;
> }
> diff --git a/src/misc/block.c b/src/misc/block.c
> index 3e953f1..fdfb89c 100644
> --- a/src/misc/block.c
> +++ b/src/misc/block.c
> @@ -520,6 +520,7 @@ struct block_fifo_t
> size_t i_depth;
> size_t i_size;
> bool b_force_wake;
> + bool b_finishing;
> };
>
> block_fifo_t *block_FifoNew( void )
> @@ -535,6 +536,7 @@ block_fifo_t *block_FifoNew( void )
> p_fifo->pp_last = &p_fifo->p_first;
> p_fifo->i_depth = p_fifo->i_size = 0;
> p_fifo->b_force_wake = false;
> + p_fifo->b_finishing = false;
>
> return p_fifo;
> }
> @@ -647,6 +649,35 @@ void block_FifoWake( block_fifo_t *p_fifo )
> }
>
> /**
> + * Set the "finishing" flag, and force the queue to wake up.
> + * This flag is intented to avoid deadlocks.
> + *
> + * @param p_fifo the queue
> + */
> +void block_FifoFinish( block_fifo_t *p_fifo )
> +{
> + vlc_mutex_lock( &p_fifo->lock );
> + p_fifo->b_force_wake = true;
> + p_fifo->b_finishing = true;
> + vlc_cond_broadcast( &p_fifo->wait );
> + vlc_mutex_unlock( &p_fifo->lock );
> +}
> +
> +/**
> + * Indicate whether the fifo is in "finishing" state.
> + *
> + * @param p_fifo the queue
> + */
> +bool block_FifoIsFinishing( block_fifo_t *p_fifo )
> +{
> + bool res;
> + vlc_mutex_lock( &p_fifo->lock );
> + res = p_fifo->b_finishing;
> + vlc_mutex_unlock( &p_fifo->lock );
> + return res;
> +}
> +
> +/**
> * Dequeue the first block from the FIFO. If necessary, wait until there is
> * one block in the queue. This function is (always) cancellation point.
> *
> @@ -667,6 +698,14 @@ block_t *block_FifoGet( block_fifo_t *p_fifo )
> vlc_cond_wait( &p_fifo->wait, &p_fifo->lock );
>
> vlc_cleanup_pop();
> +
> + if( p_fifo->b_finishing )
> + {
> + /* The fifo is finishing, we should not handle new blocks */
> + vlc_mutex_unlock( &p_fifo->lock );
> + return NULL;
> + }
> +
> b = p_fifo->p_first;
>
> p_fifo->b_force_wake = false;
> --
> 1.7.10.4
>
More information about the vlc-devel
mailing list