I got curious about this one… “server freeze” might have been an infinite loop with no pause (alln4tural’s guess), but the wait
is taking effect, so it isn’t that.
The way bind
works is:
- Before the user function, 1/ save the server’s current NetAddr in a variable and 2/ replace the server’s addr with a BundleNetAddr.
- Then run the user function. Any messages sent to the server will be collected in the BundleNetAddr.
- After the user function, 1/ reset the server’s addr to the original address; 2/ get the bundle out of the BundleNetAddr, and send it with a timestamp.
This assumes that the user function will finish immediately, so that the server can be restored to its original condition right away.
wait
inside bind
breaks that assumption – it will be collecting messages for the next 0.2 beats – the BundleNetAddr remains in place during that time.
loop
has an infinite number of repeats. So, the only way to stop this routine is from outside (cmd-. or .stop
). This can happen only when the routine is paused – and the only time the routine pauses is in the middle of bind
, when s.addr is the BundleNetAddr.
So, after stopping the routine, the server object is effectively corrupted. Running a modified version of the routine for the first time, “before” is OK but “after” is not:
(
t = Task {
s.addr.debug("before");
loop {
s.bind {
Synth(\name, [freq: x.freq].debug("args"));
wait(0.2);
};
};
}.play;
w = SimpleController(t)
.put(\stopped, {
w.remove;
s.addr.debug("after");
});
)
-> a SimpleController
// this is correct
before: a NetAddr(127.0.0.1, 57110)
args: [ freq, 220 ] // note, these are 0.2 sec apart
args: [ freq, 220 ] // ... no hang
args: [ freq, 220 ]
args: [ freq, 220 ]
args: [ freq, 220 ]
// this is not OK
after: a BundleNetAddr(127.0.0.1, 57110)
And if you run the fork block again, you’ll get “before: a BundleNetAddr(127.0.0.1, 57110)” – meaning that the server’s natural state now is to collect messages and not send them. (This will include /status
messages, with the result that editor environments that depend on sclang to poll the server’s status might think the server stopped or froze.)
I can’t think of a good way for the language to prevent this. (wait
== yield
, and yield
is used for all types of routines, not only scheduling, so it wouldn’t work to check servers’ addr objects upon yield
– not to mention that this would add overhead in a potentially high-traffic location.) AFAICS it’s just necessary to take care where to write .wait
.
hjh