Thread safety within SynthDef

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.

I’ve been using SuperCollider for 19 years, and I can’t recall any time when a user posted about having a problem with this. So, indeed… it hasn’t been a major concern.

SynthDef construction is assumed to be synchronous. (Transmission to the server is asynchronous, but building the def isn’t.) As you point out, you can break the flow in the middle, but shouldn’t.

It would be impractical for SC to prevent every potentially problematic construct – SC does allow you to shoot yourself in the foot, without requiring you to shoot yourself in the foot (but also without rescuing you in case you do).

hjh

Adding to what @jamshark70 said, also note that sclang is a single threaded program. The multi-threading that is implemented in the language with Routines (et similia) is actually a kind of green threading. There is effectively no benefit in building SynthDefs on different Routines, as you would not “gain” any more performance.