http://qs321.pair.com?node_id=351369

leriksen has asked for the wisdom of the Perl Monks concerning the following question:

I have been rereading an old (1989) MC68000 programming text Microcomputer Architecture and Programming and the author introduces a concept called "coroutines"

These, as far as I can tell are like subroutines, but they contain goto's that _move_ between invocations of the sub. When the sub is reinvoked, execution resumes at the new goto position, not the start.

Some ASCII art may help.

In pseudo pascal...

coroutine cor1 { coroutine cor2 { BEGIN ... RESUME cor2;--------->BEGIN ... ...<--------------------RESUME cor1; ... RESUME cor2;----------->... ... ...<--------------------RESUME cor1; ... RESUME cor2;----------->... ... ...<--------------------RESUME cor1; ... END; END; } }

Coroutines preserve the values of their local variables between successive calls (like statics in C).

Coroutines appear to be like subroutines with multiple entry (and exit) points

Coroutines were first mentioned by Knuth in 1973, who says he found the concept mentioned in a 1954 UNIVAC programming tip !

My question is - could (and should) you implement coroutines in Perl ?

My feeling is yes it could, but not in an obvious way. And that even though you could, you shouldn't.

I also feel that I would seriously question code that intended to use them - they seem unsafe from a reentrant point of view, and definitely tricky in threaded code. Debugging coroutine based code would seem to be 'fun', for exceedingly small values of 'fun'

Update 07052004-18:42:01- added name of original text

+++++++++++++++++
#!/usr/bin/perl
use warnings;use strict;use brain;

Replies are listed 'Best First'.
Re: Coroutines in Perl
by theorbtwo (Prior) on May 07, 2004 at 06:19 UTC

    Interestingly, perl6 allows for coroutines. Parrot will be largely based on a closely related concept, called continuations.

    (As far as I can tell, a continuation is a coroutine with it's state wrapped in an object.)

    They're extremely useful in certian cases.

    Think of a CGI script. If you have continuations, you can pretend that the entire conversation with the user takes place within one coherent program, and that you're waiting for the user to respond, instead of being re-invoked on every return.

    Think of an iterator, that was written in terms of continuing it's running, instead of calling into the same sub over and over.

    It's possible to write both things without using contations, but contuations can produce cleaner code... if used carefuly, and in the right places. (Much like threads, which are also related. Like threads, they can be tricky to get working properly, and they can produce ugly and innefficent code if used poorly.)

      The main diffrenece between coroutines and threads is that threads are meant to run in "paralel" (and they *really* do on multiprocessor machines), while coroutines are not. Therefore, when using coroutines, there is no need to worry about synchronisation, race conditions, starvation, you know ... stuff that plagues "multithreaded programming".

      For an excelent discussion of coroutines and how they work in Lua, see this paper:

      http://www.inf.puc-rio.br/~roberto/docs/corosblp.pdf

        Threads are also pre-emptive, even on a single CPU. Coroutines are cooperatively yielding.
        threads are meant to run in "paralel"..., while coroutines are not. Therefore, when using coroutines, there is no need to worry about synchronisation, race conditions, starvation, you know ...
        They do effectively run in parallel, and you can get synchronisation problems. These are a symptom of any non-linear algorithm, whether it uses threading or not.
Re: Coroutines in Perl
by Corion (Patriarch) on May 07, 2004 at 06:32 UTC

    Marc Lehmann has written Coro, a module that introduces coroutines and cooperative multitasking to Perl 5. I have toyed with it, as I find cooperative multitasking much more convenient than threading for most applications where operations can be made nonblocking for Perl.

    He used the coroutines to drive a high throughput http daemon, but I found in the module versions I used, that Perl crashed unexpectedly from time to time without any indications as to what caused the crash.

    But it sure is fun to program with coroutines, as creating a parallel version of LWP was really easy and nice with it.

      Oooh, I started salivating when staring at Coro, but alas, it fails to "make test" on OSX, even after horking around a bit with the possible configuration options.

      Anyone have a clue about how to get it to work on OSX?

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.


      update: Yes, 5.8.4 (snapshot), and OSX 10.3.3.
        It looks interesting. I may give it a shot this weekend, depending on the weather. (I'm in the process of fixing up a house that needs a little TLC, so my free time isn't what it could be.)

        I'll assume that you are on the latest and greatest version of Panther, running the latest and great 5.8.x ...

        --
        tbone1, YAPS (Yet Another Perl Schlub)
        And remember, if he succeeds, so what.
        - Chick McGee

        Hmmm.... This appears to be a problem with your perl. I am running 10.3.3 with perl 5.8.3. If I use setjump/longjump, the tests run successfully (whether or not I enable context sharing). Does Apple's perl 5.8.1RC3 work?
Re: Coroutines in Perl
by davido (Cardinal) on May 07, 2004 at 06:17 UTC
    I'm not sure if I'm missing the point, but here's one example of a routine that uses lexical scoping, named blocks and goto to invoke different segments of code in the routine each time it's called.

    use strict; use warnings; { my $location = "FIRST"; sub jumparound { goto $location; FIRST: { print "First segment.\n"; $location = "SECOND"; return; } SECOND: { print "Second segment.\n"; $location = "FIRST"; return; } } } jumparound(); jumparound(); jumparound();

    And the output is:

    First segment. Second segment. First segment.

    This snippet seems to satisfy "preserve the values of their local variables between successive calls."

    It also satisfies "appears to be like subroutines with multiple entry and exit points."

    But I haven't read Knuth (and I'm beginning to feel like it's high time I do), so I'm not sure if my snippet satisfies all of the requirements.


    Dave

Re: Coroutines in Perl
by fizbin (Chaplain) on May 07, 2004 at 18:04 UTC

    Someone already metioned that Parrot will have continuations, with which implementing coroutines is exceptionally easy. (continuations can do much more than that, though - check out the pseudo-prolog syntax you can hack onto ruby, a language with native support for continuations. Scroll to the end to see it in action)

    Note that usually with coroutines, at least with the implementations of two cooperating coroutines that I've seen (often done as a bunch of twisty complicated C macros), one routine will be the "driver", and will interact with the rest of the world in the normal fashion, and the other routine will be the "driven". For example, you'll have something like this:

    COROUTINE(int) lucas_numbers(int x0, int x1) { int temp; COROUTINE_RETURN(x0); while(1) { COROUTINE_RETURN(x1); temp = x1; x1 = x1 + x0; x0 = temp; } } void print_n_fibonacci(int n) { while (n--) { printf "%d\n", COROUTINE_INVOKE(lucas_numbers, 1, 1); } }

    In the above code, only the first routine is doing anything "strange", to our understanding. The second routine is just repeatedly invoking the first, and the first just restarts wherever it left off. Note that python's generator syntax uses a very similar style. Anyone familiar with Haskell should recognize this style too.

    Note that this use of coroutines can is to some extent obsoleted (or at least, "made less necessary") by the invention of objects. It is usually obvious how to bundle the data that the coroutine is carrying around internally between invocations - in the example, this is the x0 and x1 variables - up into an object, and have the bits of code executed in-between return calls turned into one object method. However, the most straightforward implementation of the above code as an object with a single method would result in a special case for the first return value -- as we all know, ceteris paribus, more special case means more bugs.

    If we now imagine some complicated process - say, some code that read input in some arbitrary character encoding and returned it in UTF-8 one byte at a time - we see that implementing the mess with objects can require several special cases. (Although at this point someone's bound to mention that this is trivial to implement via a state machine, which are wonderful things for separating out special cases)

    -- @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/
Re: Coroutines in Perl
by Roger (Parson) on May 07, 2004 at 17:11 UTC
    Ummm, I would most likely use multi-thread/multi-process and semaphores. I have some examples/demos for my Win32::MMF module that does something similar to this. But I am probably missing the point anyway.

    Hi Leif, long time no see, and best regards. :-)