[PR] avfilter/dnn_backend_torch: Fix race condition during filter teardown (PR #23891)

Raja-89 via ffmpeg-devel <[email protected]> Thu, 23 Jul 2026 18:05:39 -0000
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178482994042.59.9538848075342011210@29965ddac10e>
PR #23891 opened by Raja-89
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23891
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23891.patch

This PR addresses the teardown crash/deadlock issue when `batch_size > 0`.

Initially, it seemed like the deadlock was caused by `inference_done` during batching, but after debugging the core dump using GDB on the filter pipeline, I found the root cause was a multi-threading race condition during early teardown. 

When a filter is cancelled early (e.g., using `-frames:v 5` or pressing 'q'), the main thread calls `dnn_free_model_th` and destroys the `request_queue` and the Torch model. However, because the asynchronous threads are spawned with `pthread_detach`, they continue running. When the detached thread finishes inference and attempts to push the request back to the destroyed `request_queue`, it triggers a segmentation fault.

To fix this, I added a synchronization loop in `dnn_free_model_th` that forces the main thread to wait (`av_usleep`) until all allocated requests (`ctx->nireq`) have been safely returned to the queue. This guarantees that all detached threads have completely finished using the PyTorch model memory before the model is destroyed. This mirrors how the OpenVINO backend handles thread synchronization implicitly during teardown.



>From faa60a0980e65ccfb7c88c6664622c9cd30b1e22 Mon Sep 17 00:00:00 2001
From: Raja-89 <[email protected]>
Date: Thu, 23 Jul 2026 23:22:32 +0530
Subject: [PATCH] avfilter/dnn_backend_torch: Fix race condition during filter
 teardown

When the filter graph is torn down early (e.g. at EOF), the main thread frees the Torch model and destroys the request queue. If asynchronous inference threads are still active, they will crash when attempting to return their requests to the destroyed queue. This adds a synchronization loop in dnn_free_model_th to ensure all allocated requests have been safely returned to the pool before destroying the model.
---
 libavfilter/dnn/dnn_backend_torch.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libavfilter/dnn/dnn_backend_torch.cpp b/libavfilter/dnn/dnn_backend_torch.cpp
index 9ba6d61377..121adc4a66 100644
--- a/libavfilter/dnn/dnn_backend_torch.cpp
+++ b/libavfilter/dnn/dnn_backend_torch.cpp
@@ -34,6 +34,7 @@ extern "C" {
 #include "libavutil/cpu.h"
 #include "queue.h"
 #include "safe_queue.h"
+#include "libavutil/time.h"
 }
 
 typedef struct THModel {
@@ -123,6 +124,9 @@ static void dnn_free_model_th(DNNModel **model)
     th_model = (THModel *)(*model);
 
     if (th_model->request_queue) {
+        while (ff_safe_queue_size(th_model->request_queue) < th_model->ctx->nireq) {
+            av_usleep(10000);
+        }
         while (ff_safe_queue_size(th_model->request_queue) != 0) {
             THRequestItem *item = (THRequestItem *)ff_safe_queue_pop_front(th_model->request_queue);
             destroy_request_item(&item);
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]