Acme-Sort-Sleep
view release on metacpan or search on metacpan
local/lib/perl5/Future.pm view on Meta::CPAN
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 )
local/lib/perl5/Future.pm view on Meta::CPAN
already complete with a result or a failure, an exception is thrown.
=cut
sub fail
{
my $self = shift;
my ( $exception, @details ) = @_;
$_[0] or Carp::croak "$self ->fail requires an exception that is true";
if( ref $self ) {
$self->{cancelled} and return $self;
$self->{ready} and Carp::croak "${\$self->__selfstr} is already ".$self->_state." and cannot be ->fail'ed";
$self->{subs} and Carp::croak "${\$self->__selfstr} is not a leaf Future, cannot be ->fail'ed";
$self->{failure} = [ $exception, @details ];
$self->_mark_ready( "fail" );
}
else {
$self = $self->new;
$self->{ready} = 1;
$self->{ready_at} = _shortmess "fail" if DEBUG;
$self->{failure} = [ $exception, @details ];
}
if( DEBUG ) {
my $at = Carp::shortmess( "failed" );
chomp $at; $at =~ s/\.$//;
$self->{ready_at} = $at;
}
return $self;
}
sub fail_cb
{
my $self = shift;
return sub { $self->fail( @_ ) };
}
=head2 die
$future->die( $message, @details )
I<Since version 0.09.>
A convenient wrapper around C<fail>. If the exception is a non-reference that
does not end in a linefeed, its value will be extended by the file and line
number of the caller, similar to the logic that C<die> uses.
Returns the C<$future>.
=cut
sub die :method
{
my $self = shift;
my ( $exception, @details ) = @_;
if( !ref $exception and $exception !~ m/\n$/ ) {
$exception .= sprintf " at %s line %d\n", (caller)[1,2];
}
$self->fail( $exception, @details );
}
=head2 on_cancel
$future->on_cancel( $code )
If the future is not yet ready, adds a callback to be invoked if the future is
cancelled by the C<cancel> method. If the future is already ready, throws an
exception.
If the future is cancelled, the callbacks will be invoked in the reverse order
to that in which they were registered.
$on_cancel->( $future )
If passed another C<Future> instance, the passed instance will be cancelled
when the original future is cancelled. This method does nothing if the future
is already complete.
=cut
sub on_cancel
{
my $self = shift;
my ( $code ) = @_;
my $is_future = blessed( $code ) && $code->isa( "Future" );
$is_future or _callable( $code ) or
Carp::croak "Expected \$code to be callable or a Future in ->on_cancel";
$self->{ready} and return $self;
push @{ $self->{on_cancel} }, $code;
return $self;
}
=head2 is_cancelled
$cancelled = $future->is_cancelled
Returns true if the future has been cancelled by C<cancel>.
=cut
sub is_cancelled
{
my $self = shift;
return $self->{cancelled};
}
=head1 USER METHODS
These methods would primarily be used by users of asynchronous interfaces, on
objects returned by such an interface.
=cut
( run in 1.215 second using v1.01-cache-2.11-cpan-8dfa8b56332 )