Skip to content

Crash on RoUninitialize or FreeLibrary of graphicscapture.dll unless first sleeping for a while #99

@BtbN

Description

@BtbN

Hi!

We've recently added Windows.Graphics.Capture support to FFmpeg, but are facing some difficulty with the correct cleanup procedure.

Specifically, since we are a library that can't expect anything from our calling process, and also don't want to affect it adversely, we have confined all interaction with WinRT/WGC into its own thread.
That thread initially calls RoInitialize in multithreaded mode, sets up a dispatch queue for that thread, and after setting up the capture chain, just sits there and pumps messages until a custom shutdown message comes in, which makes it Close() the frame pool and capture session.

So far this all works. But when the thread is about to exit, and has cleaned up everything else, it eventually will call RoUninitialize, and at that point it just crashes most of the time (sometimes it gets through it, especially if a debugger is attached).

With some experimentation, we also found out that just adding a plain old sleep() for 100 ms or so right before RoUninit fixes this. But obviously it isn't a pretty thing to do, and also seems random and could just fail as well.
Then we figured out that just LoadLibrary()'ing graphicscapture.dll results in RoUninit never crashing. But instead now we get a crash when freeing the handle, but much much less frequent. Most of the time it exits cleanly. Seemingly because enough time passes until we actually free the library.
From the looks of it, some background thread created internally is still busy spinning down and not properly joined?
Can't think of much else that'd exhibit this behaviour that gets "fixed" with sleeping.

Here's one of the crash logs I managed to capture:

#0  0x00007fffdaf766ca in RaiseException () from C:\WINDOWS\System32\KernelBase.dll
#1  0x00007fffdac9fe9f in msvcp_win!?__ExceptionPtrRethrow@@YAXPEBX@Z () from C:\WINDOWS\System32\msvcp_win.dll
#2  0x00007fff88b46d4c in GraphicsCapture!DllGetActivationFactory () from C:\Windows\System32\GraphicsCapture.dll
#3  0x000000000a4ba518 in ?? ()
#4  0x000000000a4ba4e0 in ?? ()
#5  0x0000000000000000 in ?? ()

While this was going on, the ffmpeg capture thread was doing this:

Thread 7 (Thread 23124.0x7ce8 "fc0"):
#0  0x00007fffdd963774 in ntdll!ZwUnmapViewOfSection () from C:\WINDOWS\SYSTEM32\ntdll.dll
#1  0x00007fffdd824a86 in ntdll!RtlAvlInsertNodeEx () from C:\WINDOWS\SYSTEM32\ntdll.dll
#2  0x00007fffdd848d0a in ntdll!RtlQueryInformationActivationContext () from C:\WINDOWS\SYSTEM32\ntdll.dll
#3  0x00007fffdd84776f in ntdll!LdrUnloadDll () from C:\WINDOWS\SYSTEM32\ntdll.dll
#4  0x00007fffdaecde19 in KERNELBASE!FreeLibrary () from C:\WINDOWS\System32\KernelBase.dll
#5  0x00007fffdd1f03be in combase!CoFreeUnusedLibrariesEx () from C:\WINDOWS\System32\combase.dll
#6  0x00007fffdd1ef945 in combase!CoInitializeEx () from C:\WINDOWS\System32\combase.dll
#7  0x00007fffdd1f0b54 in combase!CoFreeUnusedLibrariesEx () from C:\WINDOWS\System32\combase.dll
#8  0x00007fffdd19de38 in combase!RoOriginateErrorW () from C:\WINDOWS\System32\combase.dll
#9  0x00007fffdd19e236 in combase!RoOriginateErrorW () from C:\WINDOWS\System32\combase.dll
#10 0x00007fffdd19e36d in combase!RoOriginateErrorW () from C:\WINDOWS\System32\combase.dll
#11 0x00007fffdd1ed707 in combase!CoUninitialize () from C:\WINDOWS\System32\combase.dll
#12 0x00007fffdd1ed559 in combase!RoUninitialize () from C:\WINDOWS\System32\combase.dll
#13 0x00007ff77cf7c6e7 in gfxcapture_uninit (avctx=<optimized out>) at libavfilter/vsrc_gfxcapture.c:390
#14 0x00007ff77b94bf76 in avfilter_free (filter=0x266d540) at libavfilter/avfilter.c:807
#15 0x00007ff77b94e624 in avfilter_graph_free (graphp=0x2c2f9b0) at libavfilter/avfiltergraph.c:128
#16 0x00007ff77b8f22bf in fg_thread_uninit (fgt=<optimized out>) at fftools/ffmpeg_filter.c:3028
#17 filter_thread (arg=0x26615c0) at fftools/ffmpeg_filter.c:3178
#18 0x00007ff77b904f97 in task_wrapper (arg=0x26661b0) at fftools/ffmpeg_sched.c:2534
#19 0x00007ff77b903acf in win32thread_worker (arg=0x26695c0) at ./compat/w32pthreads.h:79
#20 0x00007fffdc82f0ad in msvcrt!_beginthreadex () from C:\WINDOWS\System32\msvcrt.dll
#21 0x00007fffdc82f17c in msvcrt!_endthreadex () from C:\WINDOWS\System32\msvcrt.dll
#22 0x00007fffdc5ee8d7 in KERNEL32!BaseThreadInitThunk () from C:\WINDOWS\System32\kernel32.dll
#23 0x00007fffdd808d9c in ntdll!RtlUserThreadStart () from C:\WINDOWS\SYSTEM32\ntdll.dll
#24 0x0000000000000000 in ?? ()

This was without manually loading the graphicscapture.dll, so supposedly it was freeing the graphicscapture.dll.

The respective code is here:
https://github.com/FFmpeg/FFmpeg/blob/master/libavfilter/vsrc_gfxcapture_winrt.cpp#L500
All the functions prefixed wgc_ are running on the thread, and it's where largely all the relevant code is confined.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions