Commit eb5c372
Stop over-releasing the LiveView frame surface and bump to 1.0.3 (#71)
1.0.0, 1.0.1 and 1.0.2 all crash the Store build a few seconds after a
LiveView
starts presenting, with the same `AccessViolationException`
(`0xC0000005`) from
`IObjectReference.Finalize` -> `Marshal.Release` -> `GC.RunFinalizers`.
Both
previous fixes moved WinRT wrappers between apartments and neither
changed the
symptom, because this was never a threading problem.
It is a use-after-free. The finalizer in the stack is the victim: it
releases a
pointer whose reference count has already reached zero.
`GetTexture` released the frame surface twice, and has done so since
LiveView
was added, which is why neither of the two fixes since touched it:
- `IObjectReference.GetRef()` hands out one owned reference (+1).
- `ComObject.As<T>(nint)` takes ownership of the pointer it is given:
its
temporary wrapper releases it, including when the `QueryInterface`
throws (-1).
- The `finally` released it a third time (-1).
Both deltas were measured against the shipped `SharpGen.Runtime` and
`WinRT.Runtime` assemblies rather than inferred from the API names.
`TryPresent` calls this once per presented frame, the frame pool holds
two
buffers and recycles them, so at 15 fps the recycled surfaces run out of
references within seconds. The next release of the freed pointer faults,
and
whichever holder gets there last raises it — in the reported crash, the
finalizer. That matches the repro: create a LiveView, then move it or
just wait.
Pair `GetRef` with `ComObject.As` and release nothing else. Net zero per
frame.
Because `As` consumes the pointer even on a failed `QueryInterface`,
dropping
the `try`/`finally` is safe on the error path too.
The over-release is present in every build. What varies is whether it
faults —
how many references the capture pipeline itself holds on the recycled
surfaces
depends on the GPU driver, and whether the freed block is reused before
the
final release depends on the process. The unpackaged builds are very
likely
corrupting memory too and getting away with it, so the packaged-only
correlation that shaped 1.0.1 and 1.0.2 was a red herring.
The `WinRtThreading` and DispatcherQueue work from 1.0.1 and 1.0.2
stays: it is
independently correct and harmless.
`VersionPrefix` is 1.0.3 so this can go to the Store (identity
`1.0.3.0`).
Verified locally: `dotnet build Whiteboard.sln -c Release` is clean,
Core smoke
tests pass. The reference arithmetic is machine-independent, but the
crash was
only ever observed on a packaged install, so confirmation is still 1.0.3
packaged on a machine that crashed on 1.0.2.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>1 parent ff5bfba commit eb5c372
3 files changed
Lines changed: 14 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
455 | 455 | | |
456 | 456 | | |
457 | 457 | | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
458 | 468 | | |
459 | 469 | | |
460 | 470 | | |
461 | | - | |
462 | | - | |
463 | | - | |
464 | | - | |
465 | | - | |
466 | | - | |
467 | | - | |
468 | | - | |
469 | | - | |
| 471 | + | |
| 472 | + | |
470 | 473 | | |
471 | 474 | | |
472 | 475 | | |
| |||
0 commit comments