EV-Etcd
view release on metacpan or search on metacpan
t/cancel_handle_lifetime.t view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use lib 'blib/lib', 'blib/arch';
use Test::More;
BEGIN {
eval { require EV };
plan skip_all => 'EV required' if $@;
}
use EV;
use EV::Etcd;
my $etcd_available = 0;
eval {
my $c = EV::Etcd->new(endpoints => ['127.0.0.1:2379'], timeout => 2);
$c->status(sub { $etcd_available = 1 if !$_[1]; EV::break });
my $t = EV::timer(3, 0, sub { EV::break });
EV::run;
};
plan skip_all => 'etcd not available on 127.0.0.1:2379' unless $etcd_available;
# Verifies the dual-ownership lifetime: a Perl handle held past client-side
# cleanup (cancellation -> RECV completion -> cleanup_watch) must remain safe
# to call methods on. Pre-fix this would UAF.
my $client = EV::Etcd->new(endpoints => ['127.0.0.1:2379']);
# --- Watch ---
my $key = "/test_cancel_lifetime_$$";
my $watch = $client->watch($key, sub { });
ok($watch, 'watch handle created');
my $cancel_done = 0;
$watch->cancel(sub { $cancel_done = 1; EV::break });
my $t1 = EV::timer(2, 0, sub { EV::break });
EV::run;
ok($cancel_done, 'first cancel callback fired');
# Burn an EV iteration so the cancelled RECV completion is processed
# (this is when cleanup_watch would have freed wc pre-fix)
my $t2 = EV::timer(0.2, 0, sub { EV::break });
EV::run;
# Now call cancel again on the still-held handle â must not crash
my $second = 0;
$watch->cancel(sub { $second = 1; EV::break });
my $t3 = EV::timer(2, 0, sub { EV::break });
EV::run;
ok($second, 'second cancel on already-cleaned handle is safe');
# --- Keepalive ---
my $lease_id;
$client->lease_grant(10, sub { $lease_id = $_[0]->{id}; EV::break });
my $tg = EV::timer(3, 0, sub { EV::break });
EV::run;
ok($lease_id, 'lease granted');
my $ka = $client->lease_keepalive($lease_id, sub { });
ok($ka, 'keepalive handle created');
my $ka_done = 0;
$ka->cancel(sub { $ka_done = 1; EV::break });
my $tk = EV::timer(2, 0, sub { EV::break });
EV::run;
ok($ka_done, 'keepalive cancel fired');
my $ti = EV::timer(0.2, 0, sub { EV::break });
EV::run;
my $ka_second = 0;
$ka->cancel(sub { $ka_second = 1; EV::break });
my $tk2 = EV::timer(2, 0, sub { EV::break });
EV::run;
ok($ka_second, 'second keepalive cancel is safe');
# --- Observe (election) ---
my $election_name = "test-cancel-lifetime-$$";
my $observe = $client->election_observe($election_name, sub { });
ok($observe, 'observe handle created');
my $obs_done = 0;
$observe->cancel(sub { $obs_done = 1; EV::break });
my $to1 = EV::timer(2, 0, sub { EV::break });
EV::run;
ok($obs_done, 'observe cancel fired');
( run in 1.237 second using v1.01-cache-2.11-cpan-1191d43216d )