Skip to content
Browse files

[rt.cpan.org 76550] Avoid hang when no requests made.

Fixed thanks to Sergei Kozunov's bug report and test case.  A sidecar
process was created at startup, but no idle timeout was set.  These
timeouts are only set when requests happen, and in an application
where all addresses are already resolved, no timeout is set.  So the
component lingers forever.
  • Loading branch information...
1 parent accbcc5 commit 34d54dbcb61ada8b0f97b4e145723be9dc35c542 @rcaputo committed May 5, 2012
Showing with 107 additions and 11 deletions.
  1. +48 −11 lib/POE/Component/Resolver.pm
  2. +59 −0 t/rt76550.t
View
59 lib/POE/Component/Resolver.pm
@@ -4,7 +4,7 @@ use warnings;
use strict;
use POE qw(Wheel::Run Filter::Reference);
-use Carp qw(croak);
+use Carp qw(croak carp);
use Time::HiRes qw(time);
use Socket qw(unpack_sockaddr_in AF_INET AF_INET6);
use Socket::GetAddrInfo qw(:newapi getnameinfo NI_NUMERICHOST NI_NUMERICSERV);
@@ -15,6 +15,8 @@ use Exporter;
use base 'Exporter';
our (@EXPORT_OK) = qw(AF_INET AF_INET6);
+my $next_alias_index = "aaaaaaaa";
+
# Determine Perl's location, per perldoc perlvar's treatment of $^X.
use Config;
@@ -35,6 +37,7 @@ sub new {
my $max_resolvers = delete($args{max_resolvers}) || 8;
my $idle_timeout = delete($args{idle_timeout}) || 15;
+ my $debug = delete($args{debug}) || 0;
my $sidecar_program = delete($args{sidecar_program});
my $af_order = delete($args{af_order});
@@ -79,12 +82,15 @@ sub new {
}
}
- my $self = bless { }, $class;
+ my $self = bless {
+ alias => "poe_component_resolver_" . $next_alias_index++,
+ debug => $debug,
+ }, $class;
POE::Session->create(
inline_states => {
_start => \&_poe_start,
- _stop => sub { undef }, # for ASSERT_DEFAULT
+ _stop => \&_poe_stop,
_parent => sub { undef }, # for ASSERT_DEFAULT
_child => sub { undef }, # for ASSERT_DEFAULT
request => \&_poe_request,
@@ -98,13 +104,14 @@ sub new {
},
heap => {
af_order => $af_order,
- alias => "$self",
+ alias => $self->{alias},
idle_timeout => $idle_timeout,
last_request_id => 0,
max_resolvers => $max_resolvers,
requests => { },
sidecar_ring => [ ],
sidecar_program => $sidecar_program,
+ debug => $debug,
}
);
@@ -115,18 +122,27 @@ sub DESTROY {
my $self = shift;
# Can't resolve the session: it must already be gone.
- return unless $poe_kernel->alias_resolve("$self");
+ return unless $poe_kernel->alias_resolve($self->{alias});
+
+ carp "<pcr> destroying $self->{alias}" if $self->{debug};
+
+ $poe_kernel->call($self->{alias}, "shutdown");
+}
- $poe_kernel->call("$self", "shutdown");
+sub _poe_stop {
+ my $heap = $_[HEAP];
+ carp "<pcr> stopping $heap->{alias}" if $heap->{debug};
}
sub shutdown {
my $self = shift;
# Can't resolve the session: it must already be gone.
- return unless $poe_kernel->alias_resolve("$self");
+ return unless $poe_kernel->alias_resolve($self->{alias});
- $poe_kernel->call("$self", "shutdown");
+ carp "<pcr> got shutdown request for $self->{alias}" if $self->{debug};
+
+ $poe_kernel->call($self->{alias}, "shutdown");
}
# Internal POE event handler to release all resources owned by the
@@ -151,6 +167,10 @@ sub _poe_shutdown {
{ map { $_ => $request->{$_} } qw(host service misc) },
);
+ warn "<pcr> $heap->{alias} --refcount for sender $request->{sender}" if (
+ $heap->{debug}
+ );
+
$kernel->refcount_decrement($request->{sender}, __PACKAGE__);
}
@@ -174,6 +194,10 @@ sub _poe_request {
my $request_id = ++$heap->{last_request_id};
my $sender_id = $_[SENDER]->ID();
+ warn "<pcr> $heap->{alias} ++refcount for sender $sender_id" if (
+ $heap->{debug}
+ );
+
$kernel->refcount_increment($sender_id, __PACKAGE__);
_poe_setup_sidecar_ring($kernel, $heap);
@@ -206,9 +230,11 @@ sub _poe_request {
sub _poe_start {
my ($kernel, $heap) = @_[KERNEL, HEAP];
+ carp "<pcr> starting $heap->{alias}" if $heap->{debug};
+
$kernel->alias_set($heap->{alias});
- _poe_setup_sidecar_ring($kernel, $heap);
+ #_poe_setup_sidecar_ring($kernel, $heap);
undef;
}
@@ -307,11 +333,18 @@ sub resolve {
my @error = sort keys %args;
croak "unknown resolve() parameter(s): @error" if @error;
+ my $result;
croak "resolve() on shutdown resolver" unless (
- $poe_kernel->call(
- "$self", "request", $host, $service, $hints, $event, $misc
+ $result = $poe_kernel->call(
+ $self->{alias}, "request", $host, $service, $hints, $event, $misc
)
);
+
+ carp "<pcr> $self->{alias} request for host($host) service($service)" if (
+ $self->{debug}
+ );
+
+ return $result;
}
# A sidecar process has produced an error or warning. Pass it
@@ -375,6 +408,10 @@ sub _poe_sidecar_response {
{ map { $_ => $request_rec->{$_} } qw(host service misc) },
);
+ warn "<pcr> $heap->{alias} --refcount for sender $request_rec->{sender}" if (
+ $heap->{debug}
+ );
+
$kernel->refcount_decrement($request_rec->{sender}, __PACKAGE__);
# No more requests? Consder detaching sidecar.
View
59 t/rt76550.t
@@ -0,0 +1,59 @@
+#!/usr/bin/perl -w
+
+use utf8;
+
+use warnings;
+use strict;
+
+sub POE::Kernel::ASSERT_DEFAULT () { 1 }
+sub POE::Kernel::CATCH_EXCEPTIONS () { 0 }
+
+use Test::More tests => 1;
+
+use POE;
+use POE::Component::Resolver;
+
+my $responses = 0;
+
+my $resolver = POE::Component::Resolver->new(
+ max_resolvers => 1,
+ idle_timeout => 2,
+);
+
+POE::Session->create(
+ inline_states => {
+ _start => sub {
+ $_[KERNEL]->alias_set("client_session");
+ },
+ shutdown => sub {
+ $_[KERNEL]->alias_remove("client_session");
+ },
+ _stop => sub {
+ $resolver = undef;
+ },
+ },
+);
+
+# The test fails when it doesn't exit promptly (if at all).
+# Use SIGALRM to detect the condition, with enough time for even slow
+# computers to finish normally... I hope.
+
+my $timed_out = 0;
+alarm(5);
+$SIG{ALRM} = sub {
+ $timed_out = 1;
+ POE::Kernel->post( client_session => 'shutdown' );
+};
+
+POE::Kernel->run();
+
+# There is a bit of a race condition here. The alarm could go off
+# somewhere in POE::Kernel->run() as it's trying to clean up. What
+# can be done to mitigate that?
+
+alarm(0);
+
+is($timed_out, 0, "exited normally");
+
+exit;
+

0 comments on commit 34d54db

Please sign in to comment.
Something went wrong with that request. Please try again.