| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- # Perl Protothreads Version 1.04
- #
- # a lightwight pseudo-threading framework for perl that is
- # heavily inspired by Adam Dunkels protothreads for the c-language
- #
- # LICENSE AND COPYRIGHT
- #
- # Copyright (C) 2014 ntruchsess (norbert.truchsess@t-online.de)
- #
- # This program is free software; you can redistribute it and/or modify it
- # under the terms of either: the GNU General Public License as published
- # by the Free Software Foundation; or the Artistic License.
- #
- # See http://dev.perl.org/licenses/ for more information.
- #
- #PT_THREAD(sub)
- #Declare a protothread
- #
- #PT_INIT(thread)
- #Initialize a thread
- #
- #PT_BEGIN(thread);
- #Declare the start of a protothread inside the sub implementing the protothread.
- #
- #PT_WAIT_UNTIL(condition);
- #Block and wait until condition is true.
- #
- #PT_WAIT_WHILE(condition);
- #Block and wait while condition is true.
- #
- #PT_WAIT_THREAD(thread);
- #Block and wait until another protothread completes.
- #
- #PT_SPAWN(thread);
- #Spawn a child protothread and wait until it exits.
- #
- #PT_RESTART;
- #Restart the protothread.
- #
- #PT_EXIT;
- #Exit the protothread. Use PT_EXIT(value) to pass an exit-value to PT_EXITVAL
- #
- #PT_END;
- #Declare the end of a protothread.
- #
- #PT_SCHEDULE(protothread);
- #Schedule a protothread.
- #
- #PT_YIELD;
- #Yield from the current protothread.
- #
- #PT_YIELD_UNTIL(condition);
- #Yield from the current protothread until the condition is true.
- #
- #PT_RETVAL
- #return the value that has been (optionaly) passed by PT_EXIT(value)
- package ProtoThreads;
- use constant {
- PT_INITIAL => 0,
- PT_WAITING => 1,
- PT_YIELDED => 2,
- PT_EXITED => 3,
- PT_ENDED => 4,
- PT_ERROR => 5,
- PT_CANCELED => 6,
- };
- my $DEBUG=0;
- use Exporter 'import';
- @EXPORT = qw(PT_THREAD PT_INITIAL PT_WAITING PT_YIELDED PT_EXITED PT_ENDED PT_ERROR PT_CANCELED PT_INIT PT_SCHEDULE);
- @EXPORT_OK = qw();
- use Text::Balanced qw (
- extract_codeblock
- );
- sub PT_THREAD($) {
- my $method = shift;
- return bless({
- PT_THREAD_STATE => PT_INITIAL,
- PT_THREAD_POSITION => 0,
- PT_THREAD_METHOD => $method
- }, "ProtoThreads");
- }
- sub PT_INIT($) {
- my $self = shift;
- $self->{PT_THREAD_POSITION} = 0;
- $self->{PT_THREAD_STATE} = PT_INITIAL;
- delete $self->{PT_THREAD_ERROR};
- }
- sub PT_SCHEDULE(@) {
- my ($self) = @_;
- my $state = $self->{PT_THREAD_METHOD}(@_);
- return ($state == PT_WAITING or $state == PT_YIELDED);
- }
- sub PT_CANCEL($) {
- my ($self,$cause) = @_;
- $self->{PT_THREAD_POSITION} = 0;
- $self->{PT_THREAD_ERROR} = $cause;
- $self->{PT_THREAD_STATE} = PT_CANCELED;
- }
- sub PT_RETVAL() {
- my $self = shift;
- return $self->{PT_THREAD_RETURN};
- }
- sub PT_STATE() {
- my $self = shift;
- return $self->{PT_THREAD_STATE};
- }
- sub PT_CAUSE() {
- my $self = shift;
- return $self->{PT_THREAD_ERROR};
- }
- sub PT_NEXTCOMMAND($$) {
- my ($code,$command) = @_;
- if ($code =~ /$command\s*(?=\()/s) {
- if ($') {
- my $before = $`;
- my $after = $';
- my ($match,$remains,$prefix) = extract_codeblock($after,"()");
- $match =~ /(^\()(.*)(\)$)/;
- my $arg = $2 if defined $2;
- $remains =~ s/^\s*;//sg;
- return (1,$before,$arg,$remains);
- }
- }
- return undef;
- }
- use Filter::Simple;
- FILTER_ONLY
- executable => sub {
-
- my $code = $_;
- my $counter = 1;
- my ($success,$before,$arg,$after,$beforeblock);
-
- while(1) {
- ($success,$beforeblock,$arg,$after) = PT_NEXTCOMMAND($code,"PT_BEGIN");
- if ($success) {
- if ($after =~ /PT_END\s*;/s) {
- my $thread = $arg;
- my $block = $thread."->{PT_THREAD_STATE} = eval { my \$PT_YIELD_FLAG = 1; goto ".$thread."->{PT_THREAD_POSITION} if ".$thread."->{PT_THREAD_POSITION};".$`.$thread."->{PT_THREAD_POSITION} = 0; delete ".$thread."->{PT_THREAD_RETURN}; return PT_ENDED; }; if (\$\@) {".$thread."->{PT_THREAD_STATE} = PT_ERROR; ".$thread."->{PT_THREAD_ERROR} = \$\@; }; return ".$thread."->{PT_THREAD_STATE};";
- my $afterblock = $';
- while (1) {
- ($success,$before,$arg,$after) = PT_NEXTCOMMAND($block,"PT_YIELD_UNTIL");
- if ($success) {
- $block=$before."\$PT_YIELD_FLAG = 0; ".$thread."->{PT_THREAD_POSITION} = 'PT_LABEL_$counter'; PT_LABEL_$counter: return PT_YIELDED unless (\$PT_YIELD_FLAG and ($arg));".$after;
- $counter++;
- next;
- }
- if ($block =~ /PT_YIELD\s*;/s) {
- $block = $`."\$PT_YIELD_FLAG = 0; ".$thread."->{PT_THREAD_POSITION} = 'PT_LABEL_$counter'; PT_LABEL_$counter: return PT_YIELDED unless \$PT_YIELD_FLAG;".$';
- $counter++;
- next;
- }
- ($success,$before,$arg,$after) = PT_NEXTCOMMAND($block,"PT_WAIT_UNTIL");
- if ($success) {
- $block=$before.$thread."->{PT_THREAD_POSITION} = 'PT_LABEL_$counter'; PT_LABEL_$counter: return PT_WAITING unless ($arg);".$after;
- $counter++;
- next;
- }
- ($success,$before,$arg,$after) = PT_NEXTCOMMAND($block,"PT_WAIT_WHILE");
- if ($success) {
- $block=$before.$thread."->{PT_THREAD_POSITION} = 'PT_LABEL_$counter'; PT_LABEL_$counter: return PT_WAITING if ($arg);".$after;
- $counter++;
- next;
- }
- ($success,$before,$arg,$after) = PT_NEXTCOMMAND($block,"PT_WAIT_THREAD");
- if ($success) {
- $block=$before."PT_WAIT_WHILE(PT_SCHEDULE(".$arg."));".$after;
- next;
- }
- ($success,$before,$arg,$after) = PT_NEXTCOMMAND($block,"PT_SPAWN");
- if ($success) {
- $block=$before.$arg."->{PT_THREAD_POSITION} = 0; PT_WAIT_THREAD($arg);".$after;
- next;
- }
- ($success,$before,$arg,$after) = PT_NEXTCOMMAND($block,"PT_EXIT");
- if ($success) {
- $block=$before.$thread."->{PT_THREAD_POSITION} = 0; ".$thread."->{PT_THREAD_RETURN} = $arg; return PT_EXITED;".$after;
- next;
- }
- if ($block =~ /PT_EXIT(\s*;|\s+)/s) {
- $block = $`.$thread."->{PT_THREAD_POSITION} = 0; delete ".$thread."->{PT_THREAD_RETURN}; return PT_EXITED".$1.$';
- next;
- }
- if ($block =~ /PT_RESTART(\s*;|\s)/s) {
- $block = $`.$thread."->{PT_THREAD_POSITION} = 0; return PT_WAITING;".$1.$';
- next;
- }
- last;
- }
- $code = $beforeblock.$block.$afterblock;
- } else {
- die "PT_END expected"
- }
- next;
- }
- last;
- };
-
- print $code if $DEBUG;
-
- $_ = $code;
-
- };
- 1;
|