1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
# (C) Paul Evans, 2009 -- leonerd@leonerd.org.uk
package CPS::Governor::Deferred;
use strict;
use warnings;
use base qw( CPS::Governor );
our $VERSION = '0.19';
=head1 NAME
C<CPS::Governor::Deferred> - iterate at some later point
=head1 SYNOPSIS
use CPS qw( gkforeach );
use CPS::Governor::Deferred;
my $gov = CPS::Governor::Deferred->new;
gkforeach( $gov, [ 1 .. 10 ],
sub {
my ( $item, $knext ) = @_;
print "A$item ";
goto &$knext;
},
sub {},
);
gkforeach( $gov, [ 1 .. 10 ],
sub {
my ( $item, $knext ) = @_;
print "B$item ";
goto &$knext;
},
sub {},
);
$gov->flush;
=head1 DESCRIPTION
This L<CPS::Governor> allows the functions using it to delay their iteration
until some later point when the containing program invokes it. This allows two
main advantages:
=over 4
=item *
CPU-intensive operations may be split apart and mixed with other IO operations
=item *
Multiple control functions may be executed in pseudo-parallel, interleaving
iterations of each giving a kind of concurrency
=back
These are achieved by having the governor store a list of code references that
need to be invoked, rather than invoking them immediately. These references
can then be invoked later, perhaps by using an idle watcher in an event
framework.
Because each code reference hasn't yet been invoked by the time the C<again>
method is called, the original caller is free to store more pending references
with the governor. This allows multiple control functions to be interleaved,
as in the C<A> and C<B> example above.
=cut
=head1 CONSTRUCTOR
=cut
=head2 $gov = CPS::Governor::Deferred->new( %args )
Returns a new instance of a C<CPS::Governor::Deferred> object. Requires no
parameters but may take any of the following to adjust its default behaviour:
=over 8
=item defer_after => INT
If given some positive number, C<$n> then the first C<$n-1> invocations of the
C<again> method will in fact be executed immediately. Thereafter they will be
enqueued in the normal mechanism. This gives the effect that longrunning loops
will be executed in batches of C<$n>.
If not supplied then every invocation of C<again> will use the queueing
mechanism.
=back
=cut
sub new
{
my $class = shift;
my %args = @_;
my $self = $class->SUPER::new( %args );
$self->{defer_after} = $args{defer_after} || 0;
return $self;
}
sub again
{
my $self = shift;
if( $self->{defer_after} and ++$self->{count} < $self->{defer_after} ) {
my $code = shift;
# args still in @_
goto &$code;
}
$self->later( @_ );
}
sub later
{
my $self = shift;
push @{ $self->{queue} }, [ @_ ];
}
=head1 METHODS
=cut
=head2 $pending = $gov->is_pending
Returns true if at least one code reference has been stored that hasn't yet
been invoked.
=cut
sub is_pending
{
my $self = shift;
return $self->{queue} && @{ $self->{queue} } > 0;
}
=head2 $gov->prod
Invokes all of the currently-stored code references, in the order they were
stored. If any new references are stored by these, they will not yet be
invoked, but will be available for the next time this method is called.
=cut
sub prod
{
my $self = shift;
$self->{count} = 0;
my $queue = $self->{queue};
$self->{queue} = [];
foreach my $item ( @$queue ) {
my ( $code, @args ) = @$item;
$code->( @args );
}
}
=head2 $gov->flush
Repeatedly calls C<prod> until no more code references are pending.
=cut
sub flush
{
my $self = shift;
$self->prod while $self->is_pending;
}
=head1 SUBCLASS METHODS
The following methods are used internally to implement the functionality,
which may be useful to implementors of subclasses.
=cut
=head2 $gov->later( $code, @args )
Used to enqueue the C<$code> ref to be invoked later with the given C<@args>,
once it is determined this should be deferred (rather than being invoked
immediately in the case of the first few invocations when C<defer_after> is
set).
=cut
=head1 AUTHOR
Paul Evans <leonerd@leonerd.org.uk>
=cut
0x55AA;
|