[chuck-users] Why do child shreds get killed?

Manfred Brockhaus 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).

-mb

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 
> parents.
>  
> 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)
> {
>             instr.NoteOn(midiNote,1.0);
>             length => now;
>             instr.NoteOff(midiNote);
> }
>  
> 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
> 613.270.1569
>  
> Identity Management for the Windows Enterprise: See how Quest helps 
> you leverage your existing investment to solve the identity management 
> challenge!
>  
> _______________________________________________
> chuck-users mailing list
> chuck-users at lists.cs.princeton.edu
> https://lists.cs.princeton.edu/mailman/listinfo/chuck-users



More information about the chuck-users mailing list