Skip to content

Commit 3575b51

Browse files
marktermclaude
andcommitted
Fix race condition in script reload causing null pointer crash
When pressing 'apply' to reload a script, the code had a critical thread-safety issue: multiple threads were executing Lua operations on the same lua_State concurrently without proper synchronization. The race condition occurred because: 1. UI thread would load/compile new script WITHOUT holding the lock 2. Audio thread could be running script->process() at the same time 3. Both threads manipulate the same Lua stack -> corruption 4. This caused DSPScriptPosition userdata to become invalid 5. Accessing position methods (bar(), beat(), etc.) would crash with null pointer dereference The fix moves the ScopedLock acquisition to the very beginning of loadScript(), ensuring ALL Lua operations are serialized: - Script loading and compilation - DSPScript construction - Script preparation - Pointer swap - Old script cleanup (release, cleanup, destructor) This prevents any concurrent Lua state access. The tradeoff is a brief audio dropout during script reload, which is acceptable for this operation. Fixes crash: Access violation reading location 0x0000000000000000 in DSPScriptPosition::_bar() and DSPScriptPosition::_beat() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent dbbecc6 commit 3575b51

File tree

1 file changed

+9
-11
lines changed

1 file changed

+9
-11
lines changed

src/nodes/scriptnode.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ Result ScriptNode::loadScript (const String& newCode)
123123
if (result.failed())
124124
return result;
125125

126+
ScopedLock sl (lock); // Lock EVERYTHING
127+
126128
ScriptLoader loader (lua);
127129
loader.load (newCode);
128130
if (loader.hasError())
@@ -133,18 +135,14 @@ Result ScriptNode::loadScript (const String& newCode)
133135
return Result::fail ("Could not instantiate script");
134136

135137
auto newScript = std::make_unique<DSPScript> (dsp);
138+
newScript->setPlayHead (getPlayHead());
139+
if (prepared)
140+
newScript->prepare (sampleRate, blockSize);
141+
triggerPortReset();
136142

137-
if (true)
138-
{
139-
newScript->setPlayHead (getPlayHead());
140-
if (prepared)
141-
newScript->prepare (sampleRate, blockSize);
142-
triggerPortReset();
143-
ScopedLock sl (lock);
144-
if (script != nullptr)
145-
newScript->copyParameterValues (*script);
146-
script.swap (newScript);
147-
}
143+
if (script != nullptr)
144+
newScript->copyParameterValues (*script);
145+
script.swap (newScript);
148146

149147
if (newScript != nullptr)
150148
{

0 commit comments

Comments
 (0)