Acme-Sort-Sleep
view release on metacpan or search on metacpan
local/lib/perl5/Future.pm view on Meta::CPAN
argument (intended for display to humans), the second argument is a short
lowercase string that relates in some way to the kind of failure that
occurred. Following this is a list of details about that kind of failure,
whose exact arrangement or structure are determined by the failure category.
For example, L<IO::Async> and L<Net::Async::HTTP> use this convention to
indicate at what stage a given HTTP request has failed:
->fail( $message, http => ... ) # an HTTP-level error during protocol
->fail( $message, connect => ... ) # a TCP-level failure to connect a
# socket
->fail( $message, resolve => ... ) # a resolver (likely DNS) failure
# to resolve a hostname
By following this convention, a module remains consistent with other
C<Future>-based modules, and makes it easy for program logic to gracefully
handle and manage failures by use of the C<catch> method.
=head2 SUBCLASSING
This class easily supports being subclassed to provide extra behavior, such as
giving the C<get> method the ability to block and wait for completion. This
may be useful to provide C<Future> subclasses with event systems, or similar.
Each method that returns a new future object will use the invocant to
construct its return value. If the constructor needs to perform per-instance
setup it can override the C<new> method, and take context from the given
instance.
sub new
{
my $proto = shift;
my $self = $proto->SUPER::new;
if( ref $proto ) {
# Prototype was an instance
}
else {
# Prototype was a class
}
return $self;
}
If an instance provides a method called C<await>, this will be called by the
C<get> and C<failure> methods if the instance is pending.
$f->await
In most cases this should allow future-returning modules to be used as if they
were blocking call/return-style modules, by simply appending a C<get> call to
the function or method calls.
my ( $results, $here ) = future_returning_function( @args )->get;
The F<examples> directory in the distribution contains some examples of how
futures might be integrated with various event systems.
=head2 DEBUGGING
By the time a C<Future> object is destroyed, it ought to have been completed
or cancelled. By enabling debug tracing of objects, this fact can be checked.
If a future object is destroyed without having been completed or cancelled, a
warning message is printed.
$ PERL_FUTURE_DEBUG=1 perl -MFuture -E 'my $f = Future->new'
Future=HASH(0xaa61f8) was constructed at -e line 1 and was lost near -e line 0 before it was ready.
Note that due to a limitation of perl's C<caller> function within a C<DESTROY>
destructor method, the exact location of the leak cannot be accurately
determined. Often the leak will occur due to falling out of scope by returning
from a function; in this case the leak location may be reported as being the
line following the line calling that function.
$ PERL_FUTURE_DEBUG=1 perl -MFuture
sub foo {
my $f = Future->new;
}
foo();
print "Finished\n";
Future=HASH(0x14a2220) was constructed at - line 2 and was lost near - line 6 before it was ready.
Finished
A warning is also printed in debug mode if a C<Future> object is destroyed
that completed with a failure, but the object believes that failure has not
been reported anywhere.
$ PERL_FUTURE_DEBUG=1 perl -Mblib -MFuture -E 'my $f = Future->fail("Oops")'
Future=HASH(0xac98f8) was constructed at -e line 1 and was lost near -e line 0 with an unreported failure of: Oops
Such a failure is considered reported if the C<get> or C<failure> methods are
called on it, or it had at least one C<on_ready> or C<on_fail> callback, or
its failure is propagated to another C<Future> instance (by a sequencing or
converging method).
=cut
=head1 CONSTRUCTORS
=cut
=head2 new
$future = Future->new
$future = $orig->new
Returns a new C<Future> instance to represent a leaf future. It will be marked
as ready by any of the C<done>, C<fail>, or C<cancel> methods. It can be
called either as a class method, or as an instance method. Called on an
instance it will construct another in the same class, and is useful for
subclassing.
This constructor would primarily be used by implementations of asynchronous
interfaces.
=cut
# Callback flags
use constant {
CB_DONE => 1<<0, # Execute callback on done
CB_FAIL => 1<<1, # Execute callback on fail
CB_CANCEL => 1<<2, # Execute callback on cancellation
CB_SELF => 1<<3, # Pass $self as first argument
CB_RESULT => 1<<4, # Pass result/failure as a list
CB_SEQ_ONDONE => 1<<5, # Sequencing on success (->then)
CB_SEQ_ONFAIL => 1<<6, # Sequencing on failure (->else)
CB_SEQ_IMDONE => 1<<7, # $code is in fact immediate ->done result
CB_SEQ_IMFAIL => 1<<8, # $code is in fact immediate ->fail result
};
use constant CB_ALWAYS => CB_DONE|CB_FAIL|CB_CANCEL;
# Useful for identifying CODE references
sub CvNAME_FILE_LINE
{
my ( $code ) = @_;
my $cv = svref_2object( $code );
my $name = join "::", $cv->STASH->NAME, $cv->GV->NAME;
return $name unless $cv->GV->NAME eq "__ANON__";
# $cv->GV->LINE isn't reliable, as outside of perl -d mode all anon CODE
# in the same file actually shares the same GV. :(
# Walk the optree looking for the first COP
my $cop = $cv->START;
$cop = $cop->next while $cop and ref $cop ne "B::COP";
sprintf "%s(%s line %d)", $cv->GV->NAME, $cop->file, $cop->line;
}
sub _callable
{
my ( $cb ) = @_;
defined $cb and ( reftype($cb) eq 'CODE' || overload::Method($cb, '&{}') );
}
sub new
{
my $proto = shift;
return bless {
ready => 0,
callbacks => [], # [] = [$type, ...]
( DEBUG ?
( do { my $at = Carp::shortmess( "constructed" );
chomp $at; $at =~ s/\.$//;
constructed_at => $at } )
: () ),
( $TIMES ?
( btime => [ gettimeofday ] )
: () ),
}, ( ref $proto || $proto );
}
my $GLOBAL_END;
END { $GLOBAL_END = 1; }
sub DESTROY_debug {
my $self = shift;
return if $GLOBAL_END;
return if $self->{ready} and ( $self->{reported} or !$self->{failure} );
my $lost_at = join " line ", (caller)[1,2];
# We can't actually know the real line where the last reference was lost;
# a variable set to 'undef' or close of scope, because caller can't see it;
# the current op has already been updated. The best we can do is indicate
# 'near'.
if( $self->{ready} and $self->{failure} ) {
warn "${\$self->__selfstr} was $self->{constructed_at} and was lost near $lost_at with an unreported failure of: " .
$self->{failure}[0] . "\n";
}
elsif( !$self->{ready} ) {
warn "${\$self->__selfstr} was $self->{constructed_at} and was lost near $lost_at before it was ready.\n";
}
}
*DESTROY = \&DESTROY_debug if DEBUG;
=head2 done I<(class method)>
=head2 fail I<(class method)>
$future = Future->done( @values )
$future = Future->fail( $exception, @details )
I<Since version 0.26.>
Shortcut wrappers around creating a new C<Future> then immediately marking it
as done or failed.
=head2 wrap
$future = Future->wrap( @values )
I<Since version 0.14.>
If given a single argument which is already a C<Future> reference, this will
be returned unmodified. Otherwise, returns a new C<Future> instance that is
already complete, and will yield the given values.
This will ensure that an incoming argument is definitely a C<Future>, and may
be useful in such cases as adapting synchronous code to fit asynchronous
libraries driven by C<Future>.
=cut
sub wrap
{
my $class = shift;
my @values = @_;
if( @values == 1 and blessed $values[0] and $values[0]->isa( __PACKAGE__ ) ) {
return $values[0];
}
else {
return $class->done( @values );
}
}
=head2 call
$future = Future->call( \&code, @args )
I<Since version 0.15.>
A convenient wrapper for calling a C<CODE> reference that is expected to
return a future. In normal circumstances is equivalent to
$future = $code->( @args )
except that if the code throws an exception, it is wrapped in a new immediate
fail future. If the return value from the code is not a blessed C<Future>
reference, an immediate fail future is returned instead to complain about this
fact.
=cut
local/lib/perl5/Future.pm view on Meta::CPAN
=head2 cancelled_futures
@f = $future->cancelled_futures
Return a list of all the pending, ready, done, failed, or cancelled
component futures. In scalar context, each will yield the number of such
component futures.
=cut
sub pending_futures
{
my $self = shift;
$self->{subs} or Carp::croak "Cannot call ->pending_futures on a non-convergent Future";
return grep { not $_->{ready} } @{ $self->{subs} };
}
sub ready_futures
{
my $self = shift;
$self->{subs} or Carp::croak "Cannot call ->ready_futures on a non-convergent Future";
return grep { $_->{ready} } @{ $self->{subs} };
}
sub done_futures
{
my $self = shift;
$self->{subs} or Carp::croak "Cannot call ->done_futures on a non-convergent Future";
return grep { $_->{ready} and not $_->{failure} and not $_->{cancelled} } @{ $self->{subs} };
}
sub failed_futures
{
my $self = shift;
$self->{subs} or Carp::croak "Cannot call ->failed_futures on a non-convergent Future";
return grep { $_->{ready} and $_->{failure} } @{ $self->{subs} };
}
sub cancelled_futures
{
my $self = shift;
$self->{subs} or Carp::croak "Cannot call ->cancelled_futures on a non-convergent Future";
return grep { $_->{ready} and $_->{cancelled} } @{ $self->{subs} };
}
=head1 TRACING METHODS
=head2 set_label
=head2 label
$future = $future->set_label( $label )
$label = $future->label
I<Since version 0.28.>
Chaining mutator and accessor for the label of the C<Future>. This should be a
plain string value, whose value will be stored by the future instance for use
in debugging messages or other tooling, or similar purposes.
=cut
sub set_label
{
my $self = shift;
( $self->{label} ) = @_;
return $self;
}
sub label
{
my $self = shift;
return $self->{label};
}
sub __selfstr
{
my $self = shift;
return "$self" unless defined $self->{label};
return "$self (\"$self->{label}\")";
}
=head2 btime
=head2 rtime
[ $sec, $usec ] = $future->btime
[ $sec, $usec ] = $future->rtime
I<Since version 0.28.>
Accessors that return the tracing timestamps from the instance. These give the
time the instance was contructed ("birth" time, C<btime>) and the time the
result was determined (the "ready" time, C<rtime>). Each result is returned as
a two-element ARRAY ref, containing the epoch time in seconds and
microseconds, as given by C<Time::HiRes::gettimeofday>.
In order for these times to be captured, they have to be enabled by setting
C<$Future::TIMES> to a true value. This is initialised true at the time the
module is loaded if either C<PERL_FUTURE_DEBUG> or C<PERL_FUTURE_TIMES> are
set in the environment.
=cut
sub btime
{
my $self = shift;
return $self->{btime};
}
sub rtime
{
my $self = shift;
return $self->{rtime};
}
=head2 elapsed
( run in 1.154 second using v1.01-cache-2.11-cpan-39bf76dae61 )