[vlc-devel] [PATCH] Avoid deadlock on stop() using UDP on Android
Romain Vimont
rom at rom1v.com
Fri May 9 17:58:45 CEST 2014
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