The Fairlight freeze is a deadlock inside Resolve, and this release repairs the lock order.
A tester's frozen Resolve was captured live with thread apply all bt: 468 threads, four waiting on a mutex, none running audio.
| Function | Takes first | Then wants |
|---|---|---|
BMDChainFX::ResetHistory |
this+0x568 |
this+0x218 |
BMDAudioPluginImpl::PreProcess |
this+0x218 |
this+0x568 |
Same object, opposite order. Each thread waits for the lock the other holds, nothing times out, and the trigger is a mouse drag on the timeline while audio runs.
The inversion is Blackmagic's and needs no third-party plugin. What hosting changes is how long the audio thread stands inside it: PreProcess holds 0x218 across the dispatch to the hosted plugin, so a plugin bridged through yabridge holds it for a Wine round trip rather than for microseconds. That turns a rare race into one you hit every session.
The fix
BMDChainFX::ResetHistory is wrapped so it takes this+0x218 first. Both mutexes are std::recursive_mutex, so the second take costs a counter increment. Every path then agrees on the order. The patch only ever adds a lock: it cannot introduce a data race, and it costs no latency.
Every offset is read out of the library's own instructions, never written down, and four checks must pass before anything is patched. A build that moved a member still works; a build that changed shape is left alone and says so in the log.
Off with FXBRIDGE_LOCKFIX=0.
Verified, and not
Verified on 21.1.0.0014: +0x218, +0x250, +0x568, the thunk adjusts 32, two vtable slots and one thunk slot rewritten, Resolve starts and runs. The 21.0.4 layout for that function is identical, checked by offset arithmetic against the tester's stack.
Not verified: that a ResetHistory has travelled through the wrapper, and that a session which used to freeze no longer does. The wrapper logs once the first time it runs:
[fxbridge] lockfix: a thunked ResetHistory came through the wrapper, mutex taken first
Install
Replace ~/.local/share/BMDAudioPlugins/libfxbridge.so and restart Resolve. On 21.0 nothing else changes. On 21.1 you also need libfxbridge-gate.so and resolve-with-fxbridge, and Resolve must be started through that launcher.
Corrections to earlier releases
The slow-plugin theory is dead: the v0.2.12 watchdog logged no slow block at all in 134 KB of log. And "it is a spin, not a deadlock, because the process sits at 145%" was a misreading of ps, whose %CPU column is a lifetime average.
Full analysis: docs/freeze-deadlock.md.