--- linuxsampler/trunk/src/engines/common/InstrumentScriptVM.cpp 2021/06/24 11:49:31 3970 +++ linuxsampler/trunk/src/engines/common/InstrumentScriptVM.cpp 2021/06/25 09:26:00 3971 @@ -106,9 +106,37 @@ AbstractInstrumentManager* pManager = dynamic_cast(pEngineChannel->pEngine->GetInstrumentManager()); - // get new script reference - parserContext = pManager->scripts.Borrow( - { .code = text, .patchVars = patchVars }, pEngineChannel + /* + Get new script reference. + + Note: every engine channel now has its own compiled script object + (a.k.a. VMParserContext). Originally a compiled script was shared by + multiple engine channels. This was wrong: we cannot share compiled + script instances among multiple engine channels (parts), for two + reasons: + + 1. VMParserContext not only encompasses the compiled tree + presentation of the requested script, but also global variables + and we don't want those global variables to be modified by + different sampler parts, as this would not be expected behaviour + by instrument script authors. + + 2. If there is more than one sampler engine instance (e.g. if there + are multiple audio output device instances) this would even crash, + because each sampler engine instance has its own ScriptVM + instance, and a (VM)ParserContext is always tied to exactly one + ScriptVM instance. + + We would not be buying much by sharing compiled scripts anyway, as a + script usually compiles in couple microseconds and RAM usage is also + neglectable. + */ + parserContext = pManager->scripts.Borrow({ + .code = text, + .patchVars = patchVars, + .engineChannel = pEngineChannel /* unique owner of script */ + }, + pEngineChannel /* who is asking to borrow */ ); if (!parserContext->errors().empty()) { std::vector errors = parserContext->errors();