[FFmpeg-devel] tsan warning about a data race in libavcodec/h264_direct.c

Wan-Teh Chang wtc at google.com
Thu Jul 20 20:16:22 EEST 2017


Hi Ronald,

On Wed, Jul 19, 2017 at 11:50 AM, Ronald S. Bultje <rsbultje at gmail.com> wrote:
> Hi Wan-Teh,
>
> On Wed, Jul 19, 2017 at 2:31 PM, Wan-Teh Chang <wtc-at-google.com at ffmpeg.org
>> wrote:
>
>> In libavcodec/h264_direct.c, there is already an
>> await_reference_mb_row() call before the read of
>> sl->ref_list[1][0].parent->mb_type[mb_xy] at line 505:
>>
>> 487 static void pred_temp_direct_motion(const H264Context *const h,
>> H264SliceCon    text *sl,
>> 488                                     int *mb_type)
>> 489 {
>> ...
>> 501
>> 502     await_reference_mb_row(h, &sl->ref_list[1][0],
>> 503                            sl->mb_y + !!IS_INTERLACED(*mb_type));
>> 504
>> 505     if (IS_INTERLACED(sl->ref_list[1][0].parent->mb_type[mb_xy]))
>> { // AFL/A    FR/FR/FL -> AFL/FL
>>
>> This seems like the wait you suggested, but I guess it is not?
>
> Yes, but clearly it's not doing the correct thing. :-). The ""fun"" in
> these type of issues is to figure out why not. ;-).

I debugged this for fun for three hours last night. I studied other
ff_thread_await_progress() calls, especially the ones in the h264
decoder. But I couldn't figure out the meaning of the third argument
(int field) of ff_thread_await_progress(). It seems to be only used
for h264, and seems important for this tsan warning. By playing with
the third argument, I was able to come up with a patch (pasted below)
that eliminates the tsan warning and makes "make fate-h264 THREADS=4"
pass under tsan. I also played with the second argument (int
progress), but had no success.

Description of the patch:

1. The new function await_reference_mb_row_both_fields() is a variant
of await_reference_mb_row(). pred_temp_direct_motion() is changed to
call await_reference_mb_row_both_fields() instead of
await_reference_mb_row() before it reads
sl->ref_list[1][0].parent->mb_type[mb_xy].

2. If ref_field_picture is true, await_reference_mb_row_both_fields()
calls ff_thread_await_progress() with both field=0 and field=1.
(await_reference_mb_row() calls ff_thread_await_progress() with only
field=ref_field in this case.)

3. If ref_field_picture is false, await_reference_mb_row_both_fields()
calls ff_thread_await_progress() with only field=0, the same as
await_reference_mb_row().

I doubt this patch is correct, but I am publishing it to solicit
ideas. I will try to debug this more this weekend.

Thanks,
Wan-Teh Chang

diff --git a/libavcodec/h264_direct.c b/libavcodec/h264_direct.c
index a7a107c8c2..e8d3811c67 100644
--- a/libavcodec/h264_direct.c
+++ b/libavcodec/h264_direct.c
@@ -197,6 +197,25 @@ static void await_reference_mb_row(const
H264Context *const h, H264Ref *ref,
                              ref_field_picture && ref_field);
 }

+/* Waits until it is also safe to access ref->parent->mb_type[mb_xy]. */
+static void await_reference_mb_row_both_fields(const H264Context *const h,
+                                               H264Ref *ref, int mb_y)
+{
+    int ref_field_picture = ref->parent->field_picture;
+    int ref_height        = 16 * h->mb_height >> ref_field_picture;
+    int row               = 16 * mb_y >> ref_field_picture;
+
+    if (!HAVE_THREADS || !(h->avctx->active_thread_type & FF_THREAD_FRAME))
+        return;
+
+    /* FIXME: This is an educated guess. Is this right? */
+    ff_thread_await_progress(&ref->parent->tf, FFMIN(row, ref_height - 1), 0);
+    if (ref_field_picture) {
+        ff_thread_await_progress(&ref->parent->tf, FFMIN(row, ref_height - 1),
+                                 1);
+    }
+}
+
 static void pred_spatial_direct_motion(const H264Context *const h,
H264SliceContext *sl,
                                        int *mb_type)
 {
@@ -499,7 +518,7 @@ static void pred_temp_direct_motion(const
H264Context *const h, H264SliceContext

     assert(sl->ref_list[1][0].reference & 3);

-    await_reference_mb_row(h, &sl->ref_list[1][0],
+    await_reference_mb_row_both_fields(h, &sl->ref_list[1][0],
                            sl->mb_y + !!IS_INTERLACED(*mb_type));

     if (IS_INTERLACED(sl->ref_list[1][0].parent->mb_type[mb_xy])) {
// AFL/AFR/FR/FL -> AFL/FL


More information about the ffmpeg-devel mailing list