Opened 5 years ago

#1643 new defect

Custom lock manager will cause multiple thread encoding deadlock

Reported by: chinshou Owned by:
Priority: normal Component: avcodec
Version: unspecified Keywords:
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

I used av_lockmgr_register to register a custom mutex manager , then I am tring to convert a bitmap to jpeg. MJPEG encoder is multiple thread encoding capable encoder. After encoding finished, when I try to use avcodec_close to close the encoder , the program was deadlocked.

I checked the source and found that to aovid thread safe problem, ffmpeg will call custom lock manager at the beginning of avcodec_close to assure exclusive access.

av_cold int avcodec_close(AVCodecContext *avctx)
{

/* If there is a user-supplied mutex locking routine, call it. */
if (ff_lockmgr_cb) {

if ((*ff_lockmgr_cb)(&codec_mutex, AV_LOCK_OBTAIN))

return -1;

}

when multiple thread encoding , the main thread will call ff_frame_thread_encoder_free to close all the worker threads

if (avctx->internal->frame_thread_encoder && avctx->thread_count > 1) {

entangled_thread_counter --;
ff_frame_thread_encoder_free(avctx);
entangled_thread_counter ++;

}

in the ff_frame_thread_encoder_free function , it set exit flag first , then use pthread_join to wait all the worker thread to finish.

when the worker threads found the exit flag and exit the loop , it will also call avcodec_close(avctx); to close their own codec
.

static void * attribute_align_arg worker(void *v){
...

while(!c->exit){

...

pthread_mutex_lock(&c->task_fifo_mutex);

while (av_fifo_size(c->task_fifo) <= 0
c->exit) {

if(c->exit){

pthread_mutex_unlock(&c->task_fifo_mutex);
goto end;

}
pthread_cond_wait(&c->task_fifo_cond, &c->task_fifo_mutex);

}
av_fifo_generic_read(c->task_fifo, &task, sizeof(task), NULL);
pthread_mutex_unlock(&c->task_fifo_mutex);

...

}

end:

av_free(pkt);
pthread_mutex_lock(&c->buffer_mutex);
avcodec_close(avctx); <---
pthread_mutex_unlock(&c->buffer_mutex);
av_freep(&avctx);
return NULL;

}

But the global ff_lockmgr_cb mutex already acquired by the main thread. so all the worker will be blocked to wait the main thread to release the mutex, and the main thread is waiting all the worker threads , finally the program fall into deadlock.

one possible solution is to add addition parameter to avcodec_close , when the flag was set , avcodec_close will not try to acquire the global custom mutex to avoid deadlock. All the internal worker thread should call the avcodec_close with this flag setted.

I test it with 20120730 source so far.

regards

Change History (0)

Note: See TracTickets for help on using tickets.