Up until now, ff_frame_thread_init had several bugs: 1. It did not check whether the condition and mutexes could be successfully created. 2. In case an error happened when setting up the child threads, ff_frame_thread_free is called to clean up all threads set up so far, including the current one. But a half-allocated context needs special handling which ff_frame_thread_frame_free doesn't provide. Notably, if allocating the AVCodecInternal, the codec's private data or setting the options fails, the codec's close function will be called (if there is one); it will also be called if the codec's init function fails, regardless of whether the FF_CODEC_CAP_INIT_CLEANUP is set. This is not supported by all codecs; in ticket #9099 (which this commit fixes) it led to a crash.
This commit fixes both of these issues. Given that it is not documented to be save to destroy mutexes/conditions that have only been zeroed, but not initialized (or whose initialization has failed), one either needs to duplicate the code to destroy them in ff_frame_thread_init or record which mutexes/condition variables need to be destroyed and check for this in ff_frame_thread_free. This patch uses the former way for the mutexes/condition variables, but lets ff_frame_thread_free take over for all threads whose AVCodecContext could be allocated. Signed-off-by: Andreas Rheinhardt <andreas.rheinha...@gmail.com> --- After several unsuccessful attempts to make pthread_cond/mutex_init fail, I looked at the source [1] and it seems that the glibc versions of these functions can't fail at all (unless one sets nondefault attributes). Therefore this part of the code is untested, unfortunately. (Removing all pthread_mutex/cond_destroy calls does not result in any complaints from Valgrind/ASAN either; seems the glibc implementation doesn't need allocations.) [1]: https://github.com/bminor/glibc/blob/master/nptl/pthread_cond_init.c https://github.com/bminor/glibc/blob/master/nptl/pthread_mutex_init.c libavcodec/pthread_frame.c | 175 ++++++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 69 deletions(-) diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index 4429a4d59c..a10d8c1266 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -64,6 +64,12 @@ enum { STATE_SETUP_FINISHED, }; +enum { + UNINITIALIZED, ///< Thread has not been created, AVCodec->close mustn't be called + NEEDS_CLOSE, ///< AVCodec->close needs to be called + INITIALIZED, ///< Thread has been properly set up +}; + /** * Context used by codec threads and stored in their AVCodecInternal thread_ctx. */ @@ -698,27 +704,40 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) for (i = 0; i < thread_count; i++) { PerThreadContext *p = &fctx->threads[i]; + AVCodecContext *ctx = p->avctx; - pthread_mutex_lock(&p->mutex); - p->die = 1; - pthread_cond_signal(&p->input_cond); - pthread_mutex_unlock(&p->mutex); - - if (p->thread_init) - pthread_join(p->thread, NULL); - p->thread_init=0; + if (ctx->internal) { + if (p->thread_init == INITIALIZED) { + pthread_mutex_lock(&p->mutex); + p->die = 1; + pthread_cond_signal(&p->input_cond); + pthread_mutex_unlock(&p->mutex); - if (codec->close && p->avctx) - codec->close(p->avctx); + pthread_join(p->thread, NULL); + } + if (codec->close && p->thread_init != UNINITIALIZED) + codec->close(ctx); #if FF_API_THREAD_SAFE_CALLBACKS - release_delayed_buffers(p); + release_delayed_buffers(p); + for (int j = 0; j < p->released_buffers_allocated; j++) + av_frame_free(&p->released_buffers[j]); + av_freep(&p->released_buffers); #endif - av_frame_free(&p->frame); - } + if (ctx->priv_data) { + if (codec->priv_class) + av_opt_free(ctx->priv_data); + av_freep(&ctx->priv_data); + } - for (i = 0; i < thread_count; i++) { - PerThreadContext *p = &fctx->threads[i]; + av_freep(&ctx->slice_offset); + + av_buffer_unref(&ctx->internal->pool); + av_freep(&ctx->internal); + av_buffer_unref(&ctx->hw_frames_ctx); + } + + av_frame_free(&p->frame); pthread_mutex_destroy(&p->mutex); pthread_mutex_destroy(&p->progress_mutex); @@ -727,26 +746,6 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) pthread_cond_destroy(&p->output_cond); av_packet_unref(&p->avpkt); -#if FF_API_THREAD_SAFE_CALLBACKS - for (int j = 0; j < p->released_buffers_allocated; j++) - av_frame_free(&p->released_buffers[j]); - av_freep(&p->released_buffers); -#endif - - if (p->avctx) { - if (codec->priv_class) - av_opt_free(p->avctx->priv_data); - av_freep(&p->avctx->priv_data); - - av_freep(&p->avctx->slice_offset); - } - - if (p->avctx) { - av_buffer_unref(&p->avctx->internal->pool); - av_freep(&p->avctx->internal); - av_buffer_unref(&p->avctx->hw_frames_ctx); - } - av_freep(&p->avctx); } @@ -791,14 +790,19 @@ int ff_frame_thread_init(AVCodecContext *avctx) fctx->threads = av_mallocz_array(thread_count, sizeof(PerThreadContext)); if (!fctx->threads) { - av_freep(&avctx->internal->thread_ctx); - return AVERROR(ENOMEM); + err = AVERROR(ENOMEM); + goto threads_alloc_fail; } - pthread_mutex_init(&fctx->buffer_mutex, NULL); - pthread_mutex_init(&fctx->hwaccel_mutex, NULL); - pthread_mutex_init(&fctx->async_mutex, NULL); - pthread_cond_init(&fctx->async_cond, NULL); +#define PTHREAD_INIT(type, ctx, field) \ + if (err = pthread_ ## type ## _init(&ctx->field, NULL)) { \ + err = AVERROR(err); \ + goto field ## _fail; \ + } + PTHREAD_INIT(mutex, fctx, buffer_mutex) + PTHREAD_INIT(mutex, fctx, hwaccel_mutex) + PTHREAD_INIT(mutex, fctx, async_mutex) + PTHREAD_INIT(cond, fctx, async_cond) fctx->async_lock = 1; fctx->delaying = 1; @@ -806,40 +810,37 @@ int ff_frame_thread_init(AVCodecContext *avctx) if (codec->type == AVMEDIA_TYPE_VIDEO) avctx->delay = src->thread_count - 1; - for (i = 0; i < thread_count; i++) { - AVCodecContext *copy = av_malloc(sizeof(AVCodecContext)); + for (i = 0; i < thread_count; ) { PerThreadContext *p = &fctx->threads[i]; + AVCodecContext *copy; + int first = !i; - pthread_mutex_init(&p->mutex, NULL); - pthread_mutex_init(&p->progress_mutex, NULL); - pthread_cond_init(&p->input_cond, NULL); - pthread_cond_init(&p->progress_cond, NULL); - pthread_cond_init(&p->output_cond, NULL); - - p->frame = av_frame_alloc(); - if (!p->frame) { - av_freep(©); - err = AVERROR(ENOMEM); - goto error; - } + PTHREAD_INIT(mutex, p, mutex) + PTHREAD_INIT(mutex, p, progress_mutex) + PTHREAD_INIT(cond, p, input_cond) + PTHREAD_INIT(cond, p, progress_cond) + PTHREAD_INIT(cond, p, output_cond) - p->parent = fctx; - p->avctx = copy; + atomic_init(&p->state, STATE_INPUT_READY); + copy = av_memdup(src, sizeof(*src)); if (!copy) { err = AVERROR(ENOMEM); - goto error; + goto copy_fail; } + /* From now on, this PerThreadContext will be cleaned up by + * ff_frame_thread_free in case of errors. */ + i++; - *copy = *src; + p->parent = fctx; + p->avctx = copy; - copy->internal = av_malloc(sizeof(AVCodecInternal)); + copy->internal = av_memdup(src->internal, sizeof(*src->internal)); if (!copy->internal) { copy->priv_data = NULL; err = AVERROR(ENOMEM); goto error; } - *copy->internal = *src->internal; copy->internal->thread_ctx = p; copy->internal->last_pkt_props = &p->avpkt; @@ -860,30 +861,66 @@ int ff_frame_thread_init(AVCodecContext *avctx) } } - if (i) + p->frame = av_frame_alloc(); + if (!p->frame) { + err = AVERROR(ENOMEM); + goto error; + } + + if (!first) copy->internal->is_copy = 1; - if (codec->init) + if (codec->init) { err = codec->init(copy); + if (err) { + if (codec->caps_internal & FF_CODEC_CAP_INIT_CLEANUP) + p->thread_init = NEEDS_CLOSE; + goto error; + } + } + p->thread_init = NEEDS_CLOSE; - if (err) goto error; - - if (!i) + if (first) update_context_from_thread(avctx, copy, 1); atomic_init(&p->debug_threads, (copy->debug & FF_DEBUG_THREADS) != 0); err = AVERROR(pthread_create(&p->thread, NULL, frame_worker_thread, p)); - p->thread_init= !err; - if(!p->thread_init) + if (err) goto error; + p->thread_init = INITIALIZED; + continue; + + copy_fail: + pthread_cond_destroy(&p->output_cond); + output_cond_fail: + pthread_cond_destroy(&p->progress_cond); + progress_cond_fail: + pthread_cond_destroy(&p->input_cond); + input_cond_fail: + pthread_mutex_destroy(&p->progress_mutex); + progress_mutex_fail: + pthread_mutex_destroy(&p->mutex); + mutex_fail: + goto error; } return 0; error: - ff_frame_thread_free(avctx, i+1); + ff_frame_thread_free(avctx, i); + return err; +async_cond_fail: + pthread_mutex_destroy(&fctx->async_mutex); +async_mutex_fail: + pthread_mutex_destroy(&fctx->hwaccel_mutex); +hwaccel_mutex_fail: + pthread_mutex_destroy(&fctx->buffer_mutex); +buffer_mutex_fail: + av_freep(&fctx->threads); +threads_alloc_fail: + av_freep(&avctx->internal->thread_ctx); return err; } -- 2.27.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".