Hello community, here is the log from the commit of package perl-Future for openSUSE:Factory checked in at 2017-12-08 13:01:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/perl-Future (Old) and /work/SRC/openSUSE:Factory/.perl-Future.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Future" Fri Dec 8 13:01:26 2017 rev:5 rq:555057 version:0.37 Changes: -------- --- /work/SRC/openSUSE:Factory/perl-Future/perl-Future.changes 2017-06-26 15:56:18.133488703 +0200 +++ /work/SRC/openSUSE:Factory/.perl-Future.new/perl-Future.changes 2017-12-08 13:01:47.736134241 +0100 @@ -1,0 +2,32 @@ +Thu Dec 7 06:23:35 UTC 2017 - [email protected] + +- updated to 0.37 + see /usr/share/doc/packages/perl-Future/Changes + + 0.37 2017/11/28 15:39:22 + [CHANGES] + * Finally got around to removing the old Makefile.PL + + [BUGFIXES] + * Fix for convergent futures that lose strong references during + cancellation (RT120468) + * ->without_cancel shouldn't retain the originating future after + completion (RT122920) + +------------------------------------------------------------------- +Tue Nov 28 06:22:55 UTC 2017 - [email protected] + +- updated to 0.36 + see /usr/share/doc/packages/perl-Future/Changes + + 0.36 2017/11/27 22:04:52 + [CHANGES] + * Added ->retain method (RT123711) + * Fixed some typoes in docs (RT118309) + * Added ->state method (RT120759) + + [BUGFIXES] + * Ensure that ->without_cancel still strongly holds a reference to + its parent future (RT122920) + +------------------------------------------------------------------- Old: ---- Future-0.35.tar.gz New: ---- Future-0.37.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ perl-Future.spec ++++++ --- /var/tmp/diff_new_pack.bDXHl2/_old 2017-12-08 13:01:48.832094660 +0100 +++ /var/tmp/diff_new_pack.bDXHl2/_new 2017-12-08 13:01:48.832094660 +0100 @@ -17,7 +17,7 @@ Name: perl-Future -Version: 0.35 +Version: 0.37 Release: 0 %define cpan_name Future Summary: Represent an Operation Awaiting Completion @@ -31,7 +31,7 @@ BuildRequires: perl BuildRequires: perl-macros BuildRequires: perl(Carp) >= 1.25 -BuildRequires: perl(Module::Build) +BuildRequires: perl(Module::Build) >= 0.400400 BuildRequires: perl(Test::Fatal) BuildRequires: perl(Test::Identity) BuildRequires: perl(Test::More) >= 0.88 ++++++ Future-0.35.tar.gz -> Future-0.37.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/Build.PL new/Future-0.37/Build.PL --- old/Future-0.35/Build.PL 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/Build.PL 2017-11-28 16:41:25.000000000 +0100 @@ -3,8 +3,6 @@ use Module::Build; -use constant HAVE_M_B_TEST_REQUIRES => $Module::Build::VERSION >= '0.4004'; - # This version of Future contains an important bugfix around weak references # in sequence Futures. Unfortunately, a lot of existing CPAN code is known to # rely on this behaviour, and will break if this module is upgraded. @@ -51,11 +49,8 @@ my $build = Module::Build->new( module_name => 'Future', - ( HAVE_M_B_TEST_REQUIRES ? "test_requires" : "configure_requires" ) => { - 'Test::Identity' => 0, - 'Test::Fatal' => 0, - 'Test::More' => '0.88', # done_testing - 'Test::Refcount' => 0, + configure_requires => { + 'Module::Build' => "0.4004", # test_requires }, requires => { 'perl' => '5.008', # fails on 5.6 smokers; no idea why @@ -63,6 +58,12 @@ 'Test::Builder::Module' => 0, 'Time::HiRes' => 0, }, + test_requires => { + 'Test::Identity' => 0, + 'Test::Fatal' => 0, + 'Test::More' => '0.88', # done_testing + 'Test::Refcount' => 0, + }, meta_merge => { # It's unlikely at the time of writing that any CPAN client actually # pays attention to this field, but it's nice to declare it on CPAN @@ -74,7 +75,6 @@ }, auto_configure_requires => 0, # Don't add M::B license => 'perl', - create_makefile_pl => 'traditional', create_license => 1, create_readme => 1, meta_merge => { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/Changes new/Future-0.37/Changes --- old/Future-0.35/Changes 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/Changes 2017-11-28 16:41:25.000000000 +0100 @@ -1,5 +1,25 @@ Revision history for Future +0.37 2017/11/28 15:39:22 + [CHANGES] + * Finally got around to removing the old Makefile.PL + + [BUGFIXES] + * Fix for convergent futures that lose strong references during + cancellation (RT120468) + * ->without_cancel shouldn't retain the originating future after + completion (RT122920) + +0.36 2017/11/27 22:04:52 + [CHANGES] + * Added ->retain method (RT123711) + * Fixed some typoes in docs (RT118309) + * Added ->state method (RT120759) + + [BUGFIXES] + * Ensure that ->without_cancel still strongly holds a reference to + its parent future (RT122920) + 0.35 2017/06/23 20:37:57 [CHANGES] * Link to YAPC::EU talk video in SEE ALSO diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/MANIFEST new/Future-0.37/MANIFEST --- old/Future-0.35/MANIFEST 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/MANIFEST 2017-11-28 16:41:25.000000000 +0100 @@ -7,7 +7,6 @@ lib/Future/Utils.pm lib/Test/Future.pm LICENSE -Makefile.PL MANIFEST This list of files META.json META.yml @@ -35,6 +34,7 @@ t/34utils-repeat-foreach.t t/35utils-map-void.t t/36utils-map.t -t/40mutext.t +t/40mutex.t t/50test-future.t +t/90legacy.t t/99pod.t diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/META.json new/Future-0.37/META.json --- old/Future-0.35/META.json 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/META.json 2017-11-28 16:41:25.000000000 +0100 @@ -10,10 +10,15 @@ ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", - "version" : "2" + "version" : 2 }, "name" : "Future", "prereqs" : { + "configure" : { + "requires" : { + "Module::Build" : "0.4004" + } + }, "runtime" : { "requires" : { "Carp" : "1.25", @@ -34,19 +39,19 @@ "provides" : { "Future" : { "file" : "lib/Future.pm", - "version" : "0.35" + "version" : "0.37" }, "Future::Mutex" : { "file" : "lib/Future/Mutex.pm", - "version" : "0.35" + "version" : "0.37" }, "Future::Utils" : { "file" : "lib/Future/Utils.pm", - "version" : "0.35" + "version" : "0.37" }, "Test::Future" : { "file" : "lib/Test/Future.pm", - "version" : "0.35" + "version" : "0.37" } }, "release_status" : "stable", @@ -56,6 +61,6 @@ ], "x_IRC" : "irc://irc.perl.org/#io-async" }, - "version" : "0.35", - "x_serialization_backend" : "JSON::PP version 2.27400" + "version" : "0.37", + "x_serialization_backend" : "JSON::PP version 2.94" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/META.yml new/Future-0.37/META.yml --- old/Future-0.35/META.yml 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/META.yml 2017-11-28 16:41:25.000000000 +0100 @@ -7,8 +7,10 @@ Test::Identity: '0' Test::More: '0.88' Test::Refcount: '0' +configure_requires: + Module::Build: '0.4004' dynamic_config: 1 -generated_by: 'Module::Build version 0.422, CPAN::Meta::Converter version 2.150005' +generated_by: 'Module::Build version 0.422, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -17,16 +19,16 @@ provides: Future: file: lib/Future.pm - version: '0.35' + version: '0.37' Future::Mutex: file: lib/Future/Mutex.pm - version: '0.35' + version: '0.37' Future::Utils: file: lib/Future/Utils.pm - version: '0.35' + version: '0.37' Test::Future: file: lib/Test/Future.pm - version: '0.35' + version: '0.37' requires: Carp: '1.25' Test::Builder::Module: '0' @@ -35,5 +37,5 @@ resources: IRC: irc://irc.perl.org/#io-async license: http://dev.perl.org/licenses/ -version: '0.35' +version: '0.37' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/Makefile.PL new/Future-0.37/Makefile.PL --- old/Future-0.35/Makefile.PL 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/Makefile.PL 1970-01-01 01:00:00.000000000 +0100 @@ -1,17 +0,0 @@ -# Note: this file was auto-generated by Module::Build::Compat version 0.4220 -require 5.008; -use ExtUtils::MakeMaker; -WriteMakefile -( - 'NAME' => 'Future', - 'VERSION_FROM' => 'lib/Future.pm', - 'PREREQ_PM' => { - 'Carp' => '1.25', - 'Test::Builder::Module' => 0, - 'Time::HiRes' => 0 - }, - 'INSTALLDIRS' => 'site', - 'EXE_FILES' => [], - 'PL_FILES' => {} -) -; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/README new/Future-0.37/README --- old/Future-0.35/README 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/README 2017-11-28 16:41:25.000000000 +0100 @@ -219,6 +219,59 @@ blessed Future reference, an immediate fail future is returned instead to complain about this fact. +METHODS + + As there are a lare number of methods on this class, they are + documented here in several sections. + +INSPECTION METHODS + + The following methods query the internal state of a Future instance + without modifying it or otherwise causing side-effects. + + is_ready + + $ready = $future->is_ready + + Returns true on a leaf future if a result has been provided to the done + method, failed using the fail method, or cancelled using the cancel + method. + + Returns true on a convergent future if it is ready to yield a result, + depending on its component futures. + + is_done + + $done = $future->is_done + + Returns true on a future if it is ready and completed successfully. + Returns false if it is still pending, failed, or was cancelled. + + is_failed + + $failed = $future->is_failed + + Since version 0.26. + + Returns true on a future if it is ready and it failed. Returns false if + it is still pending, completed successfully, or was cancelled. + + is_cancelled + + $cancelled = $future->is_cancelled + + Returns true if the future has been cancelled by cancel. + + state + + $str = $future->state + + Since version 0.36. + + Returns a string describing the state of the future, as one of the + three states named above; namely done, failed or cancelled, or pending + if it is none of these. + IMPLEMENTATION METHODS These methods would primarily be used by implementations of @@ -281,28 +334,11 @@ cancelled when the original future is cancelled. This method does nothing if the future is already complete. - is_cancelled - - $cancelled = $future->is_cancelled - - Returns true if the future has been cancelled by cancel. - USER METHODS These methods would primarily be used by users of asynchronous interfaces, on objects returned by such an interface. - is_ready - - $ready = $future->is_ready - - Returns true on a leaf future if a result has been provided to the done - method, failed using the fail method, or cancelled using the cancel - method. - - Returns true on a convergent future if it is ready to yield a result, - depending on its component futures. - on_ready $future->on_ready( $code ) @@ -323,13 +359,6 @@ Returns the $future. - is_done - - $done = $future->is_done - - Returns true on a future if it is ready and completed successfully. - Returns false if it is still pending, failed, or was cancelled. - get @result = $future->get @@ -386,15 +415,6 @@ Returns the $future. - is_failed - - $failed = $future->is_failed - - Since version 0.26. - - Returns true on a future if it is ready and it failed. Returns false if - it is still pending, completed successfully, or was cancelled. - failure $exception = $future->failure @@ -685,6 +705,32 @@ operation that is being shared among multiple sequences; cancelling one should not prevent the others from running too. + retain + + $f = $f->retain + + Since version 0.36. + + Creates a reference cycle which causes the future to remain in memory + until it completes. Returns the invocant future. + + In normal situations, a Future instance does not strongly hold a + reference to other futures that it is feeding a result into, instead + relying on that to be handled by application logic. This is normally + fine because some part of the application will retain the top-level + Future, which then strongly refers to each of its components down in a + tree. However, certain design patterns, such as mixed Future-based and + legacy callback-based API styles might end up creating Futures simply + to attach callback functions to them. In that situation, without + further attention, the Future may get lost due to having no strong + references to it. Calling ->retain on it creates such a reference which + ensures it persists until it completes. For example: + + Future->needs_all( $fA, $fB ) + ->on_done( $on_done ) + ->on_fail( $on_fail ) + ->retain; + CONVERGENT FUTURES The following constructors all take a list of component futures, and @@ -834,10 +880,11 @@ Since version 0.28. Accessors that return the tracing timestamps from the instance. These - give the time the instance was contructed ("birth" time, btime) and the - time the result was determined (the "ready" time, rtime). Each result - is returned as a two-element ARRAY ref, containing the epoch time in - seconds and microseconds, as given by Time::HiRes::gettimeofday. + give the time the instance was constructed ("birth" time, btime) and + the time the result was determined (the "ready" time, rtime). Each + result is returned as a two-element ARRAY ref, containing the epoch + time in seconds and microseconds, as given by + Time::HiRes::gettimeofday. In order for these times to be captured, they have to be enabled by setting $Future::TIMES to a true value. This is initialised true at the @@ -865,7 +912,7 @@ This method is invoked internally by various methods that are about to save a callback CODE reference supplied by the user, to be invoked - later. The default implementation simply returns the callback agument + later. The default implementation simply returns the callback argument as-is; the method is provided to allow users to provide extra behaviour. This can be done by applying a method modifier of the around kind, so in effect add a chain of wrappers. Each wrapper can then @@ -1146,6 +1193,9 @@ SEE ALSO + * Promises - an implementation of the "Promise/A+" pattern for + asynchronous programming + * curry - Create automatic curried method call closures for any class or object diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/lib/Future/Mutex.pm new/Future-0.37/lib/Future/Mutex.pm --- old/Future-0.35/lib/Future/Mutex.pm 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/lib/Future/Mutex.pm 2017-11-28 16:41:25.000000000 +0100 @@ -8,7 +8,7 @@ use strict; use warnings; -our $VERSION = '0.35'; +our $VERSION = '0.37'; use Future; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/lib/Future/Utils.pm new/Future-0.37/lib/Future/Utils.pm --- old/Future-0.35/lib/Future/Utils.pm 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/lib/Future/Utils.pm 2017-11-28 16:41:25.000000000 +0100 @@ -8,7 +8,7 @@ use strict; use warnings; -our $VERSION = '0.35'; +our $VERSION = '0.37'; use Exporter 'import'; # Can't import the one from Exporter as it relies on package inheritance diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/lib/Future.pm new/Future-0.37/lib/Future.pm --- old/Future-0.35/lib/Future.pm 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/lib/Future.pm 2017-11-28 16:41:25.000000000 +0100 @@ -1,7 +1,7 @@ # 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, 2011-2016 -- [email protected] +# (C) Paul Evans, 2011-2017 -- [email protected] package Future; @@ -9,7 +9,7 @@ use warnings; no warnings 'recursion'; # Disable the "deep recursion" warning -our $VERSION = '0.35'; +our $VERSION = '0.37'; use Carp qw(); # don't import croak use Scalar::Util qw( weaken blessed reftype ); @@ -466,7 +466,98 @@ } } -sub _state +=head1 METHODS + +As there are a lare number of methods on this class, they are documented here +in several sections. + +=cut + +=head1 INSPECTION METHODS + +The following methods query the internal state of a Future instance without +modifying it or otherwise causing side-effects. + +=cut + +=head2 is_ready + + $ready = $future->is_ready + +Returns true on a leaf future if a result has been provided to the C<done> +method, failed using the C<fail> method, or cancelled using the C<cancel> +method. + +Returns true on a convergent future if it is ready to yield a result, +depending on its component futures. + +=cut + +sub is_ready +{ + my $self = shift; + return $self->{ready}; +} + +=head2 is_done + + $done = $future->is_done + +Returns true on a future if it is ready and completed successfully. Returns +false if it is still pending, failed, or was cancelled. + +=cut + +sub is_done +{ + my $self = shift; + return $self->{ready} && !$self->{failure} && !$self->{cancelled}; +} + +=head2 is_failed + + $failed = $future->is_failed + +I<Since version 0.26.> + +Returns true on a future if it is ready and it failed. Returns false if it is +still pending, completed successfully, or was cancelled. + +=cut + +sub is_failed +{ + my $self = shift; + return $self->{ready} && !!$self->{failure}; # boolify +} + +=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}; +} + +=head2 state + + $str = $future->state + +I<Since version 0.36.> + +Returns a string describing the state of the future, as one of the three +states named above; namely C<done>, C<failed> or C<cancelled>, or C<pending> +if it is none of these. + +=cut + +sub state { my $self = shift; return !$self->{ready} ? "pending" : @@ -502,7 +593,7 @@ if( ref $self ) { $self->{cancelled} and return $self; - $self->{ready} and Carp::croak "${\$self->__selfstr} is already ".$self->_state." and cannot be ->done"; + $self->{ready} and Carp::croak "${\$self->__selfstr} is already ".$self->state." and cannot be ->done"; $self->{subs} and Carp::croak "${\$self->__selfstr} is not a leaf Future, cannot be ->done"; $self->{result} = [ @_ ]; $self->_mark_ready( "done" ); @@ -517,9 +608,12 @@ return $self; } +my $warned_done_cb; sub done_cb { my $self = shift; + $warned_done_cb or + $warned_done_cb++, warnings::warnif( deprecated => "Future->done_cb is now deprecated; use ->curry::done or sub {...}" ); return sub { $self->done( @_ ) }; } @@ -549,7 +643,7 @@ 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->{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" ); @@ -570,9 +664,12 @@ return $self; } +my $warned_fail_cb; sub fail_cb { my $self = shift; + $warned_fail_cb or + $warned_fail_cb++, warnings::warnif( deprecated => "Future->fail_cb is now deprecated; use ->curry::fail or sub {...}" ); return sub { $self->fail( @_ ) }; } @@ -637,20 +734,6 @@ 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 @@ -658,25 +741,6 @@ =cut -=head2 is_ready - - $ready = $future->is_ready - -Returns true on a leaf future if a result has been provided to the C<done> -method, failed using the C<fail> method, or cancelled using the C<cancel> -method. - -Returns true on a convergent future if it is ready to yield a result, -depending on its component futures. - -=cut - -sub is_ready -{ - my $self = shift; - return $self->{ready}; -} - =head2 on_ready $future->on_ready( $code ) @@ -724,21 +788,6 @@ return $self; } -=head2 is_done - - $done = $future->is_done - -Returns true on a future if it is ready and completed successfully. Returns -false if it is still pending, failed, or was cancelled. - -=cut - -sub is_done -{ - my $self = shift; - return $self->{ready} && !$self->{failure} && !$self->{cancelled}; -} - =head2 get @result = $future->get @@ -856,23 +905,6 @@ return $self; } -=head2 is_failed - - $failed = $future->is_failed - -I<Since version 0.26.> - -Returns true on a future if it is ready and it failed. Returns false if it is -still pending, completed successfully, or was cancelled. - -=cut - -sub is_failed -{ - my $self = shift; - return $self->{ready} && !!$self->{failure}; # boolify -} - =head2 failure $exception = $future->failure @@ -989,9 +1021,12 @@ return $self; } +my $warned_cancel_cb; sub cancel_cb { my $self = shift; + $warned_cancel_cb or + $warned_cancel_cb++, warnings::warnif( deprecated => "Future->cancel_cb is now deprecated; use ->curry::cancel or sub {...}" ); return sub { $self->cancel }; } @@ -1487,9 +1522,45 @@ } }); + $new->{orig} = $self; # just to strongref it - RT122920 + $new->on_ready( sub { undef $_[0]->{orig} } ); + return $new; } +=head2 retain + + $f = $f->retain + +I<Since version 0.36.> + +Creates a reference cycle which causes the future to remain in memory until +it completes. Returns the invocant future. + +In normal situations, a C<Future> instance does not strongly hold a reference +to other futures that it is feeding a result into, instead relying on that to +be handled by application logic. This is normally fine because some part of +the application will retain the top-level Future, which then strongly refers +to each of its components down in a tree. However, certain design patterns, +such as mixed Future-based and legacy callback-based API styles might end up +creating Futures simply to attach callback functions to them. In that +situation, without further attention, the Future may get lost due to having no +strong references to it. Calling C<< ->retain >> on it creates such a +reference which ensures it persists until it completes. For example: + + Future->needs_all( $fA, $fB ) + ->on_done( $on_done ) + ->on_fail( $on_fail ) + ->retain; + +=cut + +sub retain +{ + my $self = shift; + return $self->on_ready( sub { undef $self } ); +} + =head1 CONVERGENT FUTURES The following constructors all take a list of component futures, and return a @@ -1570,13 +1641,13 @@ weaken( my $weakself = $self ); my $sub_on_ready = sub { - return unless $weakself; + return unless my $self = $weakself; $pending--; $pending and return; - $weakself->{result} = [ @subs ]; - $weakself->_mark_ready( "wait_all" ); + $self->{result} = [ @subs ]; + $self->_mark_ready( "wait_all" ); }; foreach my $sub ( @subs ) { @@ -1643,26 +1714,26 @@ weaken( my $weakself = $self ); my $sub_on_ready = sub { - return unless $weakself; - return if $weakself->{result} or $weakself->{failure}; # don't recurse on child ->cancel + return unless my $self = $weakself; + return if $self->{result} or $self->{failure}; # don't recurse on child ->cancel return if --$pending and $_[0]->{cancelled}; if( $_[0]->{cancelled} ) { - $weakself->{failure} = [ "All component futures were cancelled" ]; + $self->{failure} = [ "All component futures were cancelled" ]; } elsif( $_[0]->{failure} ) { - $weakself->{failure} = [ $_[0]->failure ]; + $self->{failure} = [ $_[0]->failure ]; } else { - $weakself->{result} = [ $_[0]->get ]; + $self->{result} = [ $_[0]->get ]; } foreach my $sub ( @subs ) { $sub->{ready} or $sub->cancel; } - $weakself->_mark_ready( "wait_any" ); + $self->_mark_ready( "wait_any" ); }; foreach my $sub ( @subs ) { @@ -1738,29 +1809,29 @@ weaken( my $weakself = $self ); my $sub_on_ready = sub { - return unless $weakself; - return if $weakself->{result} or $weakself->{failure}; # don't recurse on child ->cancel + return unless my $self = $weakself; + return if $self->{result} or $self->{failure}; # don't recurse on child ->cancel if( $_[0]->{cancelled} ) { - $weakself->{failure} = [ "A component future was cancelled" ]; + $self->{failure} = [ "A component future was cancelled" ]; foreach my $sub ( @subs ) { $sub->cancel if !$sub->{ready}; } - $weakself->_mark_ready( "needs_all" ); + $self->_mark_ready( "needs_all" ); } elsif( my @failure = $_[0]->failure ) { - $weakself->{failure} = \@failure; + $self->{failure} = \@failure; foreach my $sub ( @subs ) { $sub->cancel if !$sub->{ready}; } - $weakself->_mark_ready( "needs_all" ); + $self->_mark_ready( "needs_all" ); } else { $pending--; $pending and return; - $weakself->{result} = [ map { $_->get } @subs ]; - $weakself->_mark_ready( "needs_all" ); + $self->{result} = [ map { $_->get } @subs ]; + $self->_mark_ready( "needs_all" ); } }; @@ -1846,27 +1917,27 @@ weaken( my $weakself = $self ); my $sub_on_ready = sub { - return unless $weakself; - return if $weakself->{result} or $weakself->{failure}; # don't recurse on child ->cancel + return unless my $self = $weakself; + return if $self->{result} or $self->{failure}; # don't recurse on child ->cancel return if --$pending and $_[0]->{cancelled}; if( $_[0]->{cancelled} ) { - $weakself->{failure} = [ "All component futures were cancelled" ]; - $weakself->_mark_ready( "needs_any" ); + $self->{failure} = [ "All component futures were cancelled" ]; + $self->_mark_ready( "needs_any" ); } elsif( my @failure = $_[0]->failure ) { $pending and return; - $weakself->{failure} = \@failure; - $weakself->_mark_ready( "needs_any" ); + $self->{failure} = \@failure; + $self->_mark_ready( "needs_any" ); } else { - $weakself->{result} = [ $_[0]->get ]; + $self->{result} = [ $_[0]->get ]; foreach my $sub ( @subs ) { $sub->cancel if !$sub->{ready}; } - $weakself->_mark_ready( "needs_any" ); + $self->_mark_ready( "needs_any" ); } }; @@ -1994,7 +2065,7 @@ 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 +time the instance was constructed ("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>. @@ -2048,7 +2119,7 @@ This method is invoked internally by various methods that are about to save a callback CODE reference supplied by the user, to be invoked later. The default -implementation simply returns the callback agument as-is; the method is +implementation simply returns the callback argument as-is; the method is provided to allow users to provide extra behaviour. This can be done by applying a method modifier of the C<around> kind, so in effect add a chain of wrappers. Each wrapper can then perform its own wrapping logic of the @@ -2338,6 +2409,11 @@ =item * +L<Promises> - an implementation of the "Promise/A+" pattern for asynchronous +programming + +=item * + L<curry> - Create automatic curried method call closures for any class or object diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/lib/Test/Future.pm new/Future-0.37/lib/Test/Future.pm --- old/Future-0.35/lib/Test/Future.pm 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/lib/Test/Future.pm 2017-11-28 16:41:25.000000000 +0100 @@ -9,7 +9,7 @@ use warnings; use base qw( Test::Builder::Module ); -our $VERSION = '0.35'; +our $VERSION = '0.37'; our @EXPORT = qw( no_pending_futures diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/01future.t new/Future-0.37/t/01future.t --- old/Future-0.35/t/01future.t 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/t/01future.t 2017-11-28 16:41:25.000000000 +0100 @@ -19,6 +19,7 @@ is_oneref( $future, '$future has refcount 1 initially' ); ok( !$future->is_ready, '$future not yet ready' ); + is( $future->state, "pending", '$future->state before done' ); my @on_ready_args; identical( $future->on_ready( sub { @on_ready_args = @_ } ), $future, '->on_ready returns $future' ); @@ -38,6 +39,7 @@ ok( $future->is_ready, '$future is now ready' ); ok( $future->is_done, '$future is done' ); ok( !$future->is_failed, '$future is not failed' ); + is( $future->state, "done", '$future->state after done' ); is_deeply( [ $future->get ], [ result => "here" ], 'Results from $future->get' ); is( scalar $future->get, "result", 'Result from scalar $future->get' ); @@ -124,6 +126,7 @@ ok( $future->is_ready, '$future->fail marks future ready' ); ok( !$future->is_done, '$future->fail does not mark future done' ); ok( $future->is_failed, '$future->fail marks future as failed' ); + is( $future->state, "failed", '$future->state after fail' ); is( scalar $future->failure, "Something broke", '$future->failure yields exception' ); my $file = __FILE__; @@ -259,4 +262,22 @@ $f->cancel; } +# retain +{ + my @args; + foreach my $method (qw( cancel done fail )) { + my $f = Future->new; + is_oneref( $f, 'start with refcount 1' ); + + is( $f->retain, $f, '->retain returns original Future' ); + + is_refcount( $f, 2, 'refcount is now increased' ); + + ok( $f->$method( @args ), "can call ->$method" ); + is_oneref( $f, 'refcount drops when completed' ); + + push @args, 'x'; + } +} + done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/02cancel.t new/Future-0.37/t/02cancel.t --- old/Future-0.35/t/02cancel.t 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/t/02cancel.t 2017-11-28 16:41:25.000000000 +0100 @@ -6,6 +6,7 @@ use Test::More; use Test::Fatal; use Test::Identity; +use Test::Refcount; use Future; @@ -40,6 +41,7 @@ ok( $ready_f->is_cancelled, 'on_ready chained future cnacelled after cancel' ); ok( !$done_f->is_ready, 'on_done chained future not ready after cancel' ); ok( !$fail_f->is_ready, 'on_fail chained future not ready after cancel' ); + is( $future->state, "cancelled", '$future->state after ->cancel' ); like( exception { $future->get }, qr/cancelled/, '$future->get throws exception by cancel' ); @@ -102,7 +104,10 @@ # without_cancel { my $f1 = Future->new; + is_oneref( $f1, '$f1 has single reference initially' ); + my $f2 = $f1->without_cancel; + is_refcount( $f1, 2, '$f1 has two references after ->without_cancel' ); $f2->cancel; ok( !$f1->is_cancelled, '$f1 not cancelled just because $f2 is' ); @@ -112,6 +117,7 @@ ok( $f3->is_ready, '$f3 ready when $f1 is' ); is_deeply( [ $f3->get ], [ "result" ], 'result of $f3' ); + is_oneref( $f1, '$f1 has one reference after done' ); } done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/11wait_any.t new/Future-0.37/t/11wait_any.t --- old/Future-0.35/t/11wait_any.t 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/t/11wait_any.t 2017-11-28 16:41:25.000000000 +0100 @@ -149,4 +149,23 @@ '->get on empty wait_any is empty' ); } +# wait_any instance disappearing partway through cancellation (RT120468) +{ + my $f = Future->new; + + my $wait; + $wait = Future->wait_any( + $f, + my $cancelled = Future->new->on_cancel( sub { + undef $wait; + }), + ); + + is( exception { $f->done(1) }, undef, + 'no problems cancelling a Future which clears the original ->wait_any ref' ); + + ok( $cancelled->is_cancelled, 'cancellation occurred as expected' ); + ok( $f->is_done, '->wait_any is marked as done' ); +} + done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/12needs_all.t new/Future-0.37/t/12needs_all.t --- old/Future-0.35/t/12needs_all.t 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/t/12needs_all.t 2017-11-28 16:41:25.000000000 +0100 @@ -144,4 +144,23 @@ is_deeply( [ $f->get ], [], '->get on empty needs_all is empty' ); } +# weakself retention (RT120468) +{ + my $f = Future->new; + + my $wait; + $wait = Future->needs_all( + $f, + my $cancelled = Future->new->on_cancel( sub { + undef $wait; + }), + ); + + is( exception { $f->fail("oopsie\n") }, undef, + 'no problems cancelling a Future which clears the original ->needs_all ref' ); + + ok( $cancelled->is_cancelled, 'cancellation occured as expected' ); + ok( $f->is_failed, '->needs_all is marked as done' ); +} + done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/13needs_any.t new/Future-0.37/t/13needs_any.t --- old/Future-0.35/t/13needs_any.t 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/t/13needs_any.t 2017-11-28 16:41:25.000000000 +0100 @@ -197,4 +197,23 @@ '->get on empty needs_any is empty' ); } +# weakself retention (RT120468) +{ + my $f = Future->new; + + my $wait; + $wait = Future->needs_any( + $f, + my $cancelled = Future->new->on_cancel( sub { + undef $wait; + }), + ); + + is( exception { $f->done(1) }, undef, + 'no problems cancelling a Future which clears the original ->needs_any ref' ); + + ok( $cancelled->is_cancelled, 'cancellation occured as expected' ); + ok( $f->is_done, '->needs_any is marked as done' ); +} + done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/36utils-map.t new/Future-0.37/t/36utils-map.t --- old/Future-0.35/t/36utils-map.t 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/t/36utils-map.t 2017-11-28 16:41:25.000000000 +0100 @@ -44,6 +44,21 @@ is_deeply( [ $future->get ], [qw( A B C D E )], '$future->get for fmap_concat out of order' ); } +# fmap_concat concurrent above input +{ + my @subf; + my $future = fmap_concat { + return $subf[$_[0]] = Future->new; + } foreach => [ 0 .. 2 ], + concurrent => 5; + + $subf[0]->done( "A" ); + $subf[1]->done( "B" ); + $subf[2]->done( "C" ); + + is_deeply( [ $future->get ], [qw( A B C )], '$future->get for fmap_concat concurrent more than input' ); +} + # fmap_concat cancel { my $f = Future->new; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/40mutex.t new/Future-0.37/t/40mutex.t --- old/Future-0.35/t/40mutex.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Future-0.37/t/40mutex.t 2017-11-28 16:41:25.000000000 +0100 @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; + +use Future; +use Future::Mutex; + +# done +{ + my $mutex = Future::Mutex->new; + + my $f; + my $lf = $mutex->enter( sub { $f = Future->new } ); + + ok( defined $lf, '->enter returns Future' ); + ok( defined $f, '->enter on new Mutex runs code' ); + + ok( !$lf->is_ready, 'locked future not yet ready' ); + + $f->done; + ok( $lf->is_ready, 'locked future ready after $f->done' ); +} + +# done chaining +{ + my $mutex = Future::Mutex->new; + + my $f1; + my $lf1 = $mutex->enter( sub { $f1 = Future->new } ); + + my $f2; + my $lf2 = $mutex->enter( sub { $f2 = Future->new } ); + + ok( !defined $f2, 'second enter not invoked while locked' ); + + $f1->done; + ok( defined $f2, 'second enter invoked after $f1->done' ); + + $f2->done; + ok( $lf2->is_ready, 'second locked future ready after $f2->done' ); +} + +# fail chaining +{ + my $mutex = Future::Mutex->new; + + my $f1; + my $lf1 = $mutex->enter( sub { $f1 = Future->new } ); + + my $f2; + my $lf2 = $mutex->enter( sub { $f2 = Future->new } ); + + ok( !defined $f2, 'second enter not invoked while locked' ); + + $f1->fail( "oops" ); + ok( defined $f2, 'second enter invoked after $f1->fail' ); + ok( $lf1->failure, 'first locked future fails after $f1->fail' ); + + $f2->done; + ok( $lf2->is_ready, 'second locked future ready after $f2->done' ); +} + +# immediately done +{ + my $mutex = Future::Mutex->new; + + is( $mutex->enter( sub { Future->done( "result" ) } )->get, + "result", + '$mutex->enter returns immediate result' ); +} + +# immediately fail +{ + my $mutex = Future::Mutex->new; + + is( $mutex->enter( sub { Future->fail( "oops" ) } )->failure, + "oops", + '$mutex->enter returns immediate failure' ); +} + +# code dies +{ + my $mutex = Future::Mutex->new; + + is( $mutex->enter( sub { die "oopsie\n" } )->failure, + "oopsie\n", + '$mutex->enter returns immediate failure on exception' ); + + is( $mutex->enter( sub { Future->done( "unlocked" ) } )->get, + "unlocked", + '$mutex remains unlocked after exception' ); +} + +done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/40mutext.t new/Future-0.37/t/40mutext.t --- old/Future-0.35/t/40mutext.t 2017-06-23 21:39:17.000000000 +0200 +++ new/Future-0.37/t/40mutext.t 1970-01-01 01:00:00.000000000 +0100 @@ -1,97 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -use Test::More; - -use Future; -use Future::Mutex; - -# done -{ - my $mutex = Future::Mutex->new; - - my $f; - my $lf = $mutex->enter( sub { $f = Future->new } ); - - ok( defined $lf, '->enter returns Future' ); - ok( defined $f, '->enter on new Mutex runs code' ); - - ok( !$lf->is_ready, 'locked future not yet ready' ); - - $f->done; - ok( $lf->is_ready, 'locked future ready after $f->done' ); -} - -# done chaining -{ - my $mutex = Future::Mutex->new; - - my $f1; - my $lf1 = $mutex->enter( sub { $f1 = Future->new } ); - - my $f2; - my $lf2 = $mutex->enter( sub { $f2 = Future->new } ); - - ok( !defined $f2, 'second enter not invoked while locked' ); - - $f1->done; - ok( defined $f2, 'second enter invoked after $f1->done' ); - - $f2->done; - ok( $lf2->is_ready, 'second locked future ready after $f2->done' ); -} - -# fail chaining -{ - my $mutex = Future::Mutex->new; - - my $f1; - my $lf1 = $mutex->enter( sub { $f1 = Future->new } ); - - my $f2; - my $lf2 = $mutex->enter( sub { $f2 = Future->new } ); - - ok( !defined $f2, 'second enter not invoked while locked' ); - - $f1->fail( "oops" ); - ok( defined $f2, 'second enter invoked after $f1->fail' ); - ok( $lf1->failure, 'first locked future fails after $f1->fail' ); - - $f2->done; - ok( $lf2->is_ready, 'second locked future ready after $f2->done' ); -} - -# immediately done -{ - my $mutex = Future::Mutex->new; - - is( $mutex->enter( sub { Future->done( "result" ) } )->get, - "result", - '$mutex->enter returns immediate result' ); -} - -# immediately fail -{ - my $mutex = Future::Mutex->new; - - is( $mutex->enter( sub { Future->fail( "oops" ) } )->failure, - "oops", - '$mutex->enter returns immediate failure' ); -} - -# code dies -{ - my $mutex = Future::Mutex->new; - - is( $mutex->enter( sub { die "oopsie\n" } )->failure, - "oopsie\n", - '$mutex->enter returns immediate failure on exception' ); - - is( $mutex->enter( sub { Future->done( "unlocked" ) } )->get, - "unlocked", - '$mutex remains unlocked after exception' ); -} - -done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Future-0.35/t/90legacy.t new/Future-0.37/t/90legacy.t --- old/Future-0.35/t/90legacy.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Future-0.37/t/90legacy.t 2017-11-28 16:41:25.000000000 +0100 @@ -0,0 +1,52 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; + +use Future; + +# done_cb +{ + my $f = Future->new; + + my $warnings; + local $SIG{__WARN__} = sub { $warnings .= join "", @_; }; + + my $cb = $f->done_cb; + $cb->( 123 ); + + is( $f->get, 123, '$f->get after done_cb invoked' ); + like( $warnings, qr/ is now deprecated/, 'Deprecation warning occured' ); +} + +# fail_cb +{ + my $f = Future->new; + + my $warnings; + local $SIG{__WARN__} = sub { $warnings .= join "", @_; }; + + my $cb = $f->fail_cb; + $cb->( "oops\n" ); + + is( $f->failure, "oops\n", '$f->failure after fail_cb invoked' ); + like( $warnings, qr/ is now deprecated/, 'Deprecation warning occured' ); +} + +# cancel_cb +{ + my $f = Future->new; + + my $warnings; + local $SIG{__WARN__} = sub { $warnings .= join "", @_; }; + + my $cb = $f->cancel_cb; + $cb->(); + + ok( $f->is_cancelled, '$f is cancelled after cancel_cb invoked' ); + like( $warnings, qr/ is now deprecated/, 'Deprecation warning occured' ); +} + +done_testing;
