Skip to content

fix(windows): shut down IMFMediaSource deterministically to stop fatal 0xC000070A threadpool crash - #2117

Open
aacarcrash wants to merge 1 commit into
CapSoftware:mainfrom
aacarcrash:fix/windows-mf-camera-shutdown
Open

fix(windows): shut down IMFMediaSource deterministically to stop fatal 0xC000070A threadpool crash#2117
aacarcrash wants to merge 1 commit into
CapSoftware:mainfrom
aacarcrash:fix/windows-mf-camera-shutdown

Conversation

@aacarcrash

@aacarcrash aacarcrash commented Aug 12, 2026

Copy link
Copy Markdown

Fixes the Windows crash reported in #2115.

Problem

0xC000070A is ntdll's fatal threadpool assertion: a HANDLE was closed while a
threadpool wait was still registered on it. Full-dump analysis (see #2115) places the
dangling wait in Media Foundation's camera kernel-streaming layer (mfksproxy.dll,
mf.dll/mfplat.dll objects adjacent to the failed TP_WAIT).

Nothing in the repo ever calls IMFMediaSource::Shutdown(). Teardown is left to COM
refcount release, which runs on whatever thread drops the last reference and races the
KS proxy's registered waits. MF documents Shutdown-before-release as the required
teardown order for media sources.

Because camera enumeration runs continuously (spawn_devices_snapshot_emitter every
500ms→5s plus the frontend's 5s poll), fresh IMFMediaSource objects are created and
refcount-dropped several times a minute for the app's whole lifetime, so the race
window recurs constantly. On the reporting machine this crashed the app 9 times in
about an hour, including 4 seconds after launch, with no camera selected
(cameraId: null) — enumeration alone triggers it.

Fix (crates/camera-mediafoundation/src/lib.rs)

  1. MediaSourceGuard — RAII owner of the IMFMediaSource that calls Shutdown() on
    drop. Held as Arc by Device (which derives Clone), so shutdown fires exactly
    once, when the last clone drops.
  2. CaptureHandle holds a clone of the guard, so a source cannot be shut down while a
    capture engine is still using it, even if the originating Device drops
    mid-capture.
  3. Drop for DeviceSourcesIterator — releases each IMFActivate and frees the
    MFEnumDeviceSources array with CoTaskMemFree. Previously the array leaked on
    every enumeration (i.e. every few seconds, forever).

media_source also goes from pub field to private; external access already went
through Deref, so no callers change.

Verification

  • Before: stock 0.5.9 crashed 9× in ~1h on the reporting machine (Windows 11 26200,
    RTX 3070 laptop, one camera in PnP Error state — roster in Windows: fatal 0xC000070A — camera enumeration (MF/mfksproxy) closes a handle with a threadpool wait still registered (0.5.9) #2115).
  • After: a patched build on the same machine, same device roster (broken virtual
    camera re-enabled deliberately), survived multiple sessions including one ~3-hour
    session of active use (record → edit → export → idle) ending in a clean tray quit:
    zero unclean shutdowns, zero WER events, zero panics in captured logs. WER
    LocalDumps was armed at DumpType=2 the whole time and captured nothing.
  • The two captured dumps show both failure flavors expected from this race
    (STATUS_INVALID_HANDLE when the slot is empty, STATUS_ACCESS_DENIED when the
    handle slot was recycled), consistent with close-while-registered.

Happy to split the iterator leak fix into its own commit if preferred.

🤖 Generated with Claude Code


Greptile Summary

This PR makes Windows Media Foundation camera-source teardown deterministic and fixes enumeration resource leaks.

  • Adds an Arc-shared guard that calls IMFMediaSource::Shutdown when the last Device or CaptureHandle owner is released.
  • Keeps the source alive throughout active capture.
  • Releases IMFActivate entries and the MFEnumDeviceSources allocation when enumeration ends.

Confidence Score: 5/5

The PR appears safe to merge with deterministic source shutdown and enumeration cleanup correctly tied to existing ownership lifetimes.

The source remains owned through active capture, Shutdown runs before the guard’s final COM release, and iterator entries are cloned rather than moved so their new centralized cleanup does not double-release them.

Important Files Changed

Filename Overview
crates/camera-mediafoundation/src/lib.rs Adds coordinated media-source shutdown ownership and correct cleanup of Media Foundation device-enumeration allocations; no actionable changed-code defect was established.

Reviews (1): Last reviewed commit: "fix(windows): shut down IMFMediaSource d..." | Re-trigger Greptile

Context used:

…atal 0xC000070A

A handle was being closed while an ntdll threadpool wait was still
registered on it, killing the process with a non-catchable 0xC000070A.
Full-dump analysis (CapSoftware#2115) places the dangling wait in Media Foundation's
camera KS layer; nothing in the repo ever called IMFMediaSource::Shutdown(),
leaving teardown to COM refcount order, which races the KS proxy's waits.

- MediaSourceGuard: Arc-shared RAII owner that calls Shutdown() when the
  last Device clone drops
- CaptureHandle co-owns the guard so an active capture can't have its
  source shut down underneath it
- Drop for DeviceSourcesIterator: release each IMFActivate and CoTaskMemFree
  the MFEnumDeviceSources array (previously leaked on every enumeration)

Fixes the crash in CapSoftware#2115.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant