--- Coro/Coro.pm 2001/07/10 01:43:21 1.5 +++ Coro/Coro.pm 2001/07/13 13:05:38 1.7 @@ -1,6 +1,6 @@ =head1 NAME -Coro - create and manage coroutines +Coro - create and manage simple coroutines =head1 SYNOPSIS @@ -8,15 +8,17 @@ $new = new Coro sub { print "in coroutine, switching back\n"; - $Coro::main->resume; + $new->transfer($main); print "in coroutine again, switching back\n"; - $Coro::main->resume; + $new->transfer($main); }; + $main = new Coro; + print "in main, switching to coroutine\n"; - $new->resume; + $main->transfer($new); print "back in main, switch to coroutine again\n"; - $new->resume; + $main->transfer($new); print "back in main\n"; =head1 DESCRIPTION @@ -43,75 +45,79 @@ XSLoader::load Coro, $VERSION; } -=item $main - -This coroutine represents the main program. - -=item $current +=item $coro = new [$coderef [, @args]] -The current coroutine (the last coroutine switched to). The initial value is C<$main> (of course). - -=cut - -$main = $current = _newprocess { - # never being called -}; - -=item $error, $error_msg, $error_coro - -This coroutine will be called on fatal errors. C<$error_msg> and -C<$error_coro> return the error message and the error-causing coroutine, -respectively. - -=cut - -$error_msg = -$error_coro = undef; - -$error = _newprocess { - print STDERR "FATAL: $error_msg\nprogram aborted\n"; - exit 250; -}; - -=item $coro = new $coderef [, @args] - -Create a new coroutine and return it. The first C call to this -coroutine will start execution at the given coderef. If it returns it -should return a coroutine to switch to. If, after returning, the coroutine -is Cd again it starts execution again at the givne coderef. +Create a new coroutine and return it. The first C call to this +coroutine will start execution at the given coderef. If, the subroutine +returns it will be executed again. + +If the coderef is omitted this function will create a new "empty" +coroutine, i.e. a coroutine that cannot be transfered to but can be used +to save the current coroutine in. =cut sub new { my $class = $_[0]; - my $proc = $_[1]; + my $proc = $_[1] || sub { die "tried to transfer to an empty coroutine" }; bless _newprocess { do { - eval { &$proc->resume }; + eval { &$proc }; if ($@) { - ($error_msg, $error_coro) = ($@, $current); - $error->resume; + $error_msg = $@; + $error_coro = _newprocess { }; + &transfer($error_coro, $error); } } while (1); }, $class; } -=item $coro->resume +=item $prev->transfer($next) -Resume execution at the given coroutine. +Save the state of the current subroutine in C<$prev> and switch to the +coroutine saved in C<$next>. -=cut +The "state" of a subroutine only ever includes scope, i.e. lexical +variables and the current execution state. It does not save/restore any +global variables such as C<$_> or C<$@> or any other special or non +special variables. So remember that every function call that might call +C (such as C) might clobber any global +and/or special variables. Yes, this is by design ;) You cna always create +your own process abstraction model that saves these variables. + +The easiest way to do this is to create your own scheduling primitive like this: + + sub schedule { + local ($_, $@, ...); + $old->transfer($new); + } -my $prev; +=cut -# I call the _transfer function from a pelr function +# I call the _transfer function from a perl function # because that way perl saves all important things on -# the stack. -sub resume { - $prev = $current; $current = $_[0]; - _transfer($prev, $current); +# the stack. Actually, I'd do it from within XS, but +# I couldn't get it to work. +sub transfer { + _transfer($_[0], $_[1]); } +=item $error, $error_msg, $error_coro + +This coroutine will be called on fatal errors. C<$error_msg> and +C<$error_coro> return the error message and the error-causing coroutine +(NOT an object) respectively. This API might change. + +=cut + +$error_msg = +$error_coro = undef; + +$error = _newprocess { + print STDERR "FATAL: $error_msg\nprogram aborted\n"; + exit 50; +}; + 1; =back