[chuck-users] Why do child shreds get killed?
manfred.brockhaus at chello.at
Tue Sep 6 15:16:18 EDT 2005
Here is a quote from the language specification:
"in the current implementation, when a parent shred exits, all child
shreds all exit (this behavior will be enhanced in the future.)"
So obviously the developers are aware of the problem. I agree that the
current behavior is quite inconvenient, at least for traditional
programming (as opposed to on-the-fly).
On Sep 6, 2005, at 6:53 PM, Robin Davies wrote:
> Currently, if a shred terminates, all of its child shreds get
> terminated as well.
> This raises the following interesting question: why?
> Honestly, from a user’s perspective, I can’t think of a compelling use
> of this feature. Whether there are internal implementation issues that
> motivate this feature or not is a separate question, I guess; but I
> can’t really think of a good reason to terminate child shreds from an
> implementation perspective, either. (Unless you’re relying on shred
> termination to garbage collect).
> On the other hand, I can think of very compelling reasons why shreds
> should not outlive their parents.
> I’ve been bitten by this several times – by shreds silently being
> terminated because parent shreds have terminated.
> Here’s my compelling use case for allowing shreds to outlive their
> I spent the weekend playing with chuck. With audio languages (CSound,
> SAOL, Music-N, &c), one of the constant challenges is how to provide
> clean and modular integration between expensive control-rate
> operations, fast audio-rate operations, and external events.
> Having had exposure to MusicN based languages that provide
> control-rate processing, I thought I’d try using the control concept
> in chuck.
> Here’s the theory: write an IInstrument abstract class that provides
> an abstract Tick() method which gets called at control-rate. Output of
> the instrument is placed on the Instrument.Out ugen so that
> instruments can then be routed onto an external output.
> Implementations of IInstrument then call (for example)
> StartTick(16::samp), which causes the Tick method to be called every
> 16 samples on a separate shred. This works very well. The end result:
> an AnalogSynth class, implementing IInstrument, that provides two two
> control-rate lfos, two control-rate envelopes, one band-limited
> oscillator ugen (private build coming to a Chuck near you soon) and a
> filter ugen. AnalogSynth runs about 12 voices in realtime on my lowly
> 800Mhz home machine, without any real attention to optimization! So
> far so good. The purpose of the Tick() processing is,of course, to
> run the LFOs and ADSrs at reduced rate (every 16 samples), and then
> perform all of the expensive routing of lfos and ADSrs outputs onto
> UGens at something less than audio rate.
> Well. It does work very well, as long as I drive the IInstrument from
> midi or OSC input. It fails spectacularly if notes are triggered from
> temporary shreds when writing scores in Chuck. Consider the following:
> function void PlayNote(IInstrument instr, float midNote, dur length)
> length => now;
> spork ~ PlayNote(analogSynth, Midi.C4, 1.5::beat);
> This fails, spectacularly. Internally, the Tick shred gets started up,
> but it shuts down as soon as the PlayNote shred terminates (causing
> the note to get stuck).
> In retrospect, I guess the answer is to create a tick shred at the
> time the instrument is created, and then use events to synchronize the
> tick shred. But that’s just so… so… inconvenient. Or another approach:
> make PlayNote wait forever, after the note off. But that’s just so…
> so…. ugly. Hmm. Or maybe a WaitForAllChildThreadsToDie() function.
> The event solution also runs into significant problems with
> controlling the first tick after startup. Consider: An IInstrument
> receives a note-on event. It initializes all of it’s control rate
> state variables (reseting ADSRs, for example). Now it needs to start
> the tick shred up. So it signals an event. The question is this: will
> the tick shred run in the current cycle or not? If it doesn’t then
> StartTick() should call IInstrument.Tick(). But whether the shred ran
> in the current thread or not is non-deterministic. It depends on
> whether the shreduler ran the current thread or the tick thread first
> for the current value of Now. Even worse. The tick shred can no longer
> perform “16::samp => now”, because the noteon event may have arrived
> at some non-modulo-8 mutiple of now::samp. So this approach requires
> complicated and expensive handshaking between the Instrument and its
> tick thread to get everything lined up and operating
> deterministically. (<thinking>… this is doable, and probably the way
> I’ll go)
> Or another use-case for the same:
> function void PlayTheWholeSong()
> Spork ~ PlayBassMotif1();
> Spork ~ PlayMelody1();
> 12::beat => now;
> Spork ~ PlayBassMotif1();
> Spork ~ PlayMelody2();
> // oops. No more bass motif or melody shred.
> The feature request; maybe a way to let shreds re-assign their parent
> shred to the root shred. That would be nice. Or maybe just don’t
> terminate child shreds at all.
> Robin Davies
> Lead Software Developer
> Quest Web Reports
> Quest Software
> Identity Management for the Windows Enterprise: See how Quest helps
> you leverage your existing investment to solve the identity management
> chuck-users mailing list
> chuck-users at lists.cs.princeton.edu
More information about the chuck-users