[chuck-users] Chuck Concurrency Best Practices

Spencer Salazar ssalazar at CS.Princeton.EDU
Sun Aug 31 04:00:55 EDT 2008


Mike,
What if each Forkable has/is an Event that is then broadcasted when  
the Forkable completes?  Additionally, a Forkable or Forker could  
maintain a list of Forkables it has forked, and ensure that each has  
completed before exiting itself.  Below is a quick and dirty sketch of  
how this might work, combining Forker/Forkable into a single class.

As far as pass-by-reference/pass-by-value, I hold the opinion that  
copying objects is not something that should be implicit in the  
semantics of an OO system.  Mainly because there is always the  
question of just what is copied and what isn't copied in a default  
copy operation (shallow vs. deep copy).  Shallow bit-wise copies will  
often break the assumptions made in the class, giving you a broken  
object.  Ditto member-by-member copies.  I would claim that its very  
difficult or impossible to correctly generalize copying semantics  
across the entire body of allowable classes.  Moreover, if such a  
generalization does exist, it would be too complicated too remember,  
causing frequent documentation hits or writing a custom copy function.

Thus, making copies is something that should be explicitly written on  
a case-by-case basis for a given class -- with full knowledge of the  
semantics of the class and the desired semantics of the copy  
operation.  This is something where I believe Java gets it right --  
the .clone() member function is protected by default, and must be  
explicitly publicized by subclasses that override it.

So, in long-winded way, I'm suggesting that its not too hard to  
introduce pass-by-value semantics to your code, provided that you  
write functions to do the copying.  In the future ChucK could  
conceivably support a notion of a copy constructor, but it would need  
to support constructors in general first :)

spencer

// Forkable sketch

class Forkable extends Event
{
     0 => int _isFinished;
     Forkable @ children[0];

     // override this in subclasses
     fun void run()
     {

     }

     // private; executes run() and performs shred initialization/ 
cleanup
     fun void runShell()
     {
         run();

         for(0 => int i; i < children.size(); i++)
         {
             children[i].wait();
         }

         1 => _isFinished;
         this.broadcast();
     }

     // call this to spork/fork the Forkable
     fun void start()
     {
         spork ~ runShell();
     }

     // returns 1 if this Forkable has completely finished executing;  
0 otherwise
     fun int isFinished()
     {
         return _isFinished;
     }

     // protected; starts a new Forkable as a child Forkable.  This  
Forkable is not considered finished
     // and thus will not exit until all child Forkables are finished.
     fun void spawn(Forkable @ forkable)
     {
         children << forkable;
         forkable.start();
     }

     // block/pass time until Forkable has finished
     fun void wait()
     {
         if(!isFinished())
             this => now;
     }
}

class DumbExample2 extends Forkable
{
     static int lastId;
     lastId++ => int id;

     fun void run()
     {
         Std.rand2f(1,5)::second => now;
         <<< "done in DumbExample2:", id, "at", now / second, "s" >>>;
     }
}

0 => DumbExample2.lastId;

class DumbExample extends Forkable
{
     fun void run()
     {
         spawn(new DumbExample2);
         spawn(new DumbExample2);
         spawn(new DumbExample2);
         spawn(new DumbExample2);

         Std.rand2f(1,5)::second => now;
         <<< "done in DumbExample", "at", now / second, "s" >>>;
     }
}

DumbExample f;

f.start();

// do stuff...
// ...
// ...

// okay, now we're done, let's make sure that all child shreds are  
done before exiting

f.wait();

<<< "child Forkable is finished, exiting", "at", now / second, "s" >>>;

// end Forkable sketch

On Aug 29, 2008, at 4:01 PM, mike clemow wrote:

> Hi everyone,
>
> So, I've been struggling to find a method to leverage the  
> concurrency model in Chuck and I keep running up against the same  
> issue: my guts tell me to do things that I can't do because of fact  
> that child processes are alive only as long as the parent and the  
> fact that objects are only passed by reference.
>
> Let me unpack this for you.  I'm building a granular synthesis class  
> library for Chuck.  Most everything is being cast as one of two  
> types of objects:
>
> 1) Forkable - a process that takes parameters and can be run or  
> sporked
> 2) Forker - an object that knows how run a process and will fork it  
> out for you.
>
> In order to get over the fact that functions are not (yet) datatypes  
> in Chuck which can be passed to other functions, I'm basically  
> following the example that Kassen, Mike, et al. were discussing in  
> this forum topic:
>
> http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546
>
> My problems always seem to rise from the fact that every Forker  
> (whose job it is to fork a process) also has to be aware of how long  
> that process is going to take or else run the risk of dying before  
> it's child has completed.  This leads to infinite passing of  
> responsibility for time and every Forker seems to also need to be a  
> Forkable and have it's own Forker, et cetera ad inifinitum.  At  
> higher and higher levels of abstraction, finding the duration of a  
> process BEFORE it runs and figuring out how to handle it, is  
> requiring more and more analysis and feels...  for lack of a better  
> word, "wrong" to me.
>
> This is compounded by the fact that the objects I'm passing into  
> these functions (methods of other objects) can only be passed by  
> reference and it's increasingly difficult to set-and-forget these  
> processes, since they constantly reference other global objects.   
> The passing by reference is fine, but I find myself wishing I had  
> the choice to make a copy.
>
> Now, it's entirely probable that I just haven't caught on yet and am  
> going about this process in a completely backward way.  Does anyone  
> here run into similar issues and (even better) have good methods of  
> avoiding them?
>
> One idea I have considered is to make a Forker a looping process  
> that receives Events as input, rather than an object with methods.   
> As this would require a(nother) complete re-write, I'm stalling.  ;-)
>
> So, how do you manage your concurrent processes in Chuck?
>
> Cheers,
> Michael
>
> PS: Love dynamic Arrays!  Can't wait for those SC/Smalltalk-like  
> array methods!
>
>
> -- 
> http://semiotech.org/wp-prod/
> http://semiotech.org/michael
> _______________________________________________
> 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