SClang is cooperatively multi-threaded. The SynthDef graph builder actually relies on this a fair bit by saving the global state of the current graph being built in some classvar
in UGen. The following rather unusual code shows how one might see weird results if one yields from within a SynthDef. Normally one would not write code like that, but if you call from your SynthDef functions something that yields and you don’t know it… all bets are off.
\hahaha.kr // normally this is an error (outside SynthDef)
// ERROR: Message 'addKr' not understood. RECEIVER: nil
(r = Routine {
SynthDef(\heh, { Out.ar(0, 0); "silly".yield } ).yield
})
r.next
\hahaha.kr // this actually goes into the SynthDef
d = r.next
d.allControlNames
This could even be a feature, but if you somehow build SynthDefs on more than one thread in your program, you need to be careful not to allow a thread switch before a SynthDef is fully assembled.
// More of a bug-like situation
(r = Routine {
SynthDef(\heh, { \some.kr; "silly".yield;
\lost.kr; Out.ar(0, 0); }).yield
})
r.next
// Another thread starts building another SynthDef:
z = SynthDef(\nope, { \hmmm.kr; Out.ar(0, 0); })
z.allControlNames
z.dumpUGens // ok, not confused, a new graph was started
d = r.next
// ERROR: Message 'addKr' not understood. RECEIVER: nil
// The SythDef context started in Routine 'r' was destroyed
// when 'z' was completed, so resuming 'r' now causes error
// at \lost.kr as if issued outside SynthDef.
This is almost certainly not a concern to most users though.