Description
Skyrim SE 1.5.97 has a engine bug in its worker dispatcher. Two internal spinlocks (called LockA and LockB inside the binary) can be acquired in opposite orders by two worker threads at the same time. When that happens, neither thread can make progress, the main thread waits forever for a worker acknowledgement that never arrives, and the game hard-freezes.
WorkerSpinLockFix is a small SKSE plugin that fixes that specific bug. It does not improve FPS, ENB performance, scripts, save sizes, AI, animations, or anything else. It addresses exactly one engine bug: the AB-BA spinlock inversion in the worker pool.
If your Skyrim freezes randomly during normal play — combat, movement, shouts, archery, casting — and Task Manager is the only way out, there is a real chance this is the bug you are hitting and this plugin will fix it.
Main features
Structural fix for the AB-BA inversion (primary, default). One inline hook on the LockA-acquirer plus two surgical call-site patches inside the engine's worker-dispatch cycle hub. LockB-acquirer calls made while LockA is held are deferred onto a thread-local queue and replayed once LockA is released, so the AB-BA cycle simply cannot form in the first place.
Runtime breaker as defence-in-depth (default). A single inline hook on BSSpinLock::Acquire with a lock-free per-thread wait-for graph. If a cycle ever forms on a path the structural fix doesn't cover, the breaker confirms it across a configurable window (default 2 ms) and force-releases the lock via a single InterlockedCompareExchange. With the structural fix healthy, this layer is expected to never have to fire — but it is there as a safety net.
Surgical, not blanket. The slow path runs only for the two specific engine spinlocks involved in the bug. Every other BSSpinLock::Acquire in the engine pays one pointer compare and a tail call (~2 ns at 3 GHz). No measurable performance impact on the hot path.
Call-site patching, not function-wraps, for the LockB acquirers. Function entries are left pristine, so other mods that hook those functions continue to work. Pre-patch byte verification refuses to install if another mod has already redirected the same call sites.
Optional stale-owner reaper backstop. Catches the rare case where a thread dies still holding a lock another thread is waiting on. As of v2.0.3, this layer no longer suspends any engine thread. It reads the wait-graph populated by the runtime breaker and uses GetExitCodeThread to test owner liveness. Off by default-
Telemetry. Once per minute (configurable, or off) the plugin emits a single counter line. Healthy signature is phase4_queued > 0, breaks_done = 0: the structural fix is preempting cycles and the runtime breaker has nothing to clean up. Every cycle the breaker actually operates on emits a structured [CYCLE] / [BREAK] block, including thread IDs, lock addresses, and observed owners — auditable after the fact.
Per-layer escape hatches. Each layer has its own kill-switch in the TOML. If anything misbehaves you can disable layers individually instead of removing the whole plugin: [phase4_defer] enabled = false, [acquire_hook] enabled = false, [breaker] break_enabled = false (detect-only mode), or [plugin] enabled = false (entirely inert without uninstalling).
Compatible with Skyrim Cell load Freeze fix NG . Different engine primitives (BSSpinLock vs BSReadWriteLock), different call sites, no overlap. Run both if you have both freeze classes.
Instructions
just download and install the fix using MO2
if you also want to share logs when a freeze happens also install the freezelogger
Requirements
WorkerSpinLockFix
the fix itself
FreezeLogger
a tool which creates an engine dump when the game freezes