Repository navigation
v1.0.9
NeewerLux v1.0.9 Release Notes
Bug Fix
Animation Chain Revert — Thread Race Condition Fixed
Fixed: Chained animations (triggering Animation 2 while Animation 1 is playing) would fail to revert to the original pre-animation state, instead freezing on the final frame of the last animation. Single-shot animations reverted correctly.
Root cause: A time-of-check-time-of-use (TOCTOU) race between the dying animation thread and the main thread. When startAnimation() interrupted a running animation, it set a _animChainStop flag to tell the dying thread "don't revert." But stopAnimation() only waited for animationRunning = False — the dying thread set that flag before reaching the revert check. By the time the dying thread actually checked _animChainStop, the main thread had already cleared it, so the revert ran anyway, wiping preAnimationStates.
Fix: The dying thread now captures _animChainStop into a local variable (isChaining) before setting animationRunning = False. The revert decision is made from the local copy, which is immune to subsequent clearing by either the main thread or the new animation thread. The flag is only cleared at the start of the new animation thread, after the old thread has already captured its value.
Timeline of this fix:
- v1.0.5: First attempt —
wasRunningguard to skip re-capturing states (incomplete, dying thread still cleared them) - v1.0.6: Second attempt —
_animChainStopflag (fix was lost during merge and never committed) - v1.0.8: Re-applied
_animChainStop(still had the TOCTOU race) - v1.0.9: fix with local variable capture before
animationRunning = False
Full Changelog: https://github.com/poizenjam/NeewerLux/blob/main/CHANGELOG.md
Repository: https://github.com/poizenjam/NeewerLux