Commit 0f1e1e4e authored by sikeda's avatar sikeda
Browse files

[feature] Now daemons capture SIGCHLD signal so that defunct child processes...

[feature] Now daemons capture SIGCHLD signal so that defunct child processes (sendmail) will be reaped faster.


git-svn-id: https://subversion.renater.fr/sympa/branches/sympa-6.2-branch@12680 05aa8bb8-cd2b-0410-b1d7-8918dfa770ce
parent a3742406
......@@ -67,13 +67,8 @@ sub _new_instance {
#sub mail_forward($message, $from, $rcpt, $robot);
#DEPRECATED: This is no longer used.
# OBSOLETED. Use Sympa::Process::reap_child().
sub reaper {
my $self = shift;
my %options = @_;
return $process->reap_child(%options, children => $self->{_pids});
}
# DEPRECATED. Use Sympa::Process::reap_child().
#sub reaper;
#DEPRECATED.
#sub sendto;
......@@ -144,7 +139,7 @@ sub store {
# Check how many open smtp's we have, if too many wait for a few
# to terminate and then do our job.
$process->reap_child(children => $self->{_pids});
$process->sync_child(hash => $self->{_pids});
$log->syslog('debug3', 'Open = %s', scalar keys %{$self->{_pids}});
while ($maxsmtp < scalar keys %{$self->{_pids}}) {
$log->syslog(
......@@ -153,11 +148,8 @@ sub store {
scalar keys %{$self->{_pids}}
);
# Blockng call to the reaper.
last
if $process->reap_child(
blocking => 1,
children => $self->{_pids}
) < 0;
last if $process->wait_child < 0;
$process->sync_child(hash => $self->{_pids});
}
my ($pipein, $pipeout, $pid);
......@@ -283,8 +275,6 @@ Sympa::Mailer - Store messages to sendmail
$mailer->store($message, ['user1@dom.ain', user2@other.dom.ain']);
$process->reap_child;
=head1 DESCRIPTION
L<Sympa::Mailer> implements the class to invoke sendmail processes and
......@@ -305,7 +295,7 @@ A new L<Sympa::Mailer> instance, or I<undef> for failure.
=item reaper ( [ blocking =E<gt> 1 ] )
OBSOLETED.
DEPRECATED.
Use L<Sympa::Process/"reap_child">.
I<Instance method>.
......
......@@ -26,6 +26,7 @@ package Sympa::Process;
use strict;
use warnings;
use Config qw();
use English qw(-no_match_vars);
use POSIX qw();
......@@ -39,6 +40,19 @@ use Sympa::Tools::File;
use base qw(Class::Singleton);
BEGIN {
# Check compliance to POSIX.
die 'Safe signal is not provided'
unless $Config::Config{d_sigaction};
die 'Non-blocking wait is not supported'
unless $Config::Config{d_waitpid}
or $Config::Config{d_wait4};
}
INIT {
register_handler();
}
my $log = Sympa::Log->instance;
# Constructor for Class::Singleton.
......@@ -107,38 +121,16 @@ sub fork {
$pid;
}
# Old name: Sympa::Mailer::reaper().
sub reap_child {
# Old name: (part of) Sympa::Mailer::reaper().
sub wait_child {
my $self = shift;
my %options = @_;
my $pid;
my $blocking = $options{blocking};
while (0 < ($pid = waitpid(-1, $blocking ? 0 : POSIX::WNOHANG()))) {
$blocking = 0;
unless (exists $self->{children}->{$pid}) {
$log->syslog('debug2', 'Reaper waited %s, unknown process to me',
$pid);
next;
}
if ($CHILD_ERROR & 127) {
$log->syslog(
'err',
'Child process %s for <%s> was terminated by signal %d',
$pid,
$self->{children}->{$pid},
$CHILD_ERROR & 127
);
} elsif ($CHILD_ERROR) {
$log->syslog(
'err', 'Child process %s for <%s> exited with status %s',
$pid,
$self->{children}->{$pid},
$CHILD_ERROR >> 8
);
}
delete $self->{children}->{$pid};
my $nohang = 0;
while (0 < ($pid = waitpid(-1, $nohang))) {
$nohang = POSIX::WNOHANG();
$self->_reap_child($pid);
}
$log->syslog(
'debug3',
......@@ -147,11 +139,62 @@ sub reap_child {
scalar keys %{$self->{children}}
);
if ($options{children}) {
my $children = $options{children};
foreach my $child_pid (keys %$children) {
delete $children->{$child_pid}
unless exists $self->{children}->{$child_pid};
return $pid;
}
sub register_handler {
$SIG{CHLD} = \&_child_handler;
$SIG{PIPE} = 'IGNORE';
}
sub _child_handler {
# Don't change $! and $? outside handler.
local ($ERRNO, $CHILD_ERROR);
# Reap only children registered by fork().
my $self = __PACKAGE__->instance;
foreach my $pid (keys %{$self->{children}}) {
next unless 0 < waitpid($pid, POSIX::WNOHANG());
$self->_reap_child($pid);
}
# For SysV signal(2).
$SIG{CHLD} = \&_child_handler;
}
sub _reap_child {
my $self = shift;
my $pid = shift;
my $for =
(exists $self->{children}->{$pid})
? $self->{children}->{$pid}
: 'unknown';
if ($CHILD_ERROR & 127) {
$log->syslog('err',
'Child process %s for <%s> was terminated by signal %d',
$pid, $for, $CHILD_ERROR & 127);
} elsif ($CHILD_ERROR) {
$log->syslog('err', 'Child process %s for <%s> exited with status %s',
$pid, $for, $CHILD_ERROR >> 8);
} else {
$log->syslog('debug2', 'Child process %s for <%s> exited normally',
$pid, $for);
}
delete $self->{children}->{$pid};
}
sub sync_child {
my $self = shift;
my %options = @_;
if ($options{hash}) {
my $hash = $options{hash};
foreach my $child_pid (keys %$hash) {
next
if exists $self->{children}->{$child_pid}
and kill 0, $child_pid;
delete $hash->{$child_pid};
}
}
......@@ -159,19 +202,19 @@ sub reap_child {
foreach my $child_pid (_get_pids_in_pid_file($self->{pidname})) {
next if $child_pid == $PID;
unless (exists $self->{children}->{$child_pid}) {
$log->syslog(
'err',
'The %s child exists in the PID file but is no longer running. Removing it and notifying listmaster',
$child_pid
);
$self->remove_pid(pid => $child_pid);
_send_crash_report($child_pid);
}
next
if exists $self->{children}->{$child_pid}
and kill 0, $child_pid;
$log->syslog(
'err',
'The %s child exists in the PID file but is no longer running. Removing it and notifying listmaster',
$child_pid
);
$self->remove_pid(pid => $child_pid);
_send_crash_report($child_pid);
}
}
return $pid;
}
# Moved to Log::_daemon_name().
......@@ -475,13 +518,18 @@ Sympa::Process - Process of Sympa
$process->daemonize;
$process->fork;
$process->reap_child;
=head1 DESCRIPTION
L<Sympa::Process> implements the class to handle process itself of Sympa
software.
=head2 Signal handling
Once L<Sympa::Process> is loaded,
C<SIGCHLD> signals are captured,
and only defunct child processes invoked by fork() method are reaped.
=head2 Methods
=over
......@@ -536,25 +584,35 @@ Returns:
See L<perlfunc/"fork">.
=item reap_child ( [ blocking =E<gt> 1 ], [ children =E<gt> \%children ],
file =E<gt> 1 ] )
=item reap_child ( [ blocking =E<gt> 1 ] )
DEPRECATED.
=item wait_child ( )
I<Instance method>.
Non blocking function called by: main loop of sympa, task_manager, bounced
etc., just to clean the defuncts list by waiting to any processes and
decrementing the counter.
Waits for any child process.
Parameter:
Parameters:
=over
None.
Returns:
C<0>.
Returns C<-1> on failure.
=item sync_child ( [ hash =E<gt> \%hash ], [ file =E<gt> 1 ] )
=item blocking =E<gt> 1
Updates process information in external data.
Operation would block.
Parameters:
=over
=item children =E<gt> \%children
=item hash =E<gt> \%hash
Syncs PIDs in local map %children.
Syncs PIDs in local map %hash
=item file =E<gt> 1
......@@ -565,9 +623,7 @@ If dead PID is found, notification will be sent to super-listmaster.
Returns:
PID of reaped child porocess or C<0>.
If blocking option is set, returns C<0>.
In both cases returns C<-1> on failure.
None.
=item remove_pid ([ pid =E<gt> $pid ], [ final =E<gt> 1 ] )
......@@ -635,6 +691,11 @@ Evaluate subroutine $subref in $timeout seconds.
TBD.
=item register_handler ( )
Registers C<SIGCHLD> handler.
This function is usually called automatically during initialization.
=back
=head1 HISTORY
......@@ -646,4 +707,8 @@ and began to provide OO interface.
Sympa 6.2.13 introduced daemonize() method and {detached} attribute.
As of Sympa 6.2.14, C<SIGCHLD> signal was captured and child processes
were reaped immediately. reap_child() method (formerly reaper()) was
deprecated.
=cut
......@@ -53,8 +53,8 @@ sub _init {
# Process grouped notifications.
Sympa::Alarm->instance->flush;
} elsif ($state == 2) {
# Free zombie sendmail process.
Sympa::Process->instance->reap_child;
## Free zombie sendmail process.
#Sympa::Process->instance->reap_child;
}
1;
......
......@@ -55,8 +55,8 @@ sub _init {
# Process grouped notifications.
Sympa::Alarm->instance->flush;
} elsif ($state == 2) {
# Free zombie sendmail process.
Sympa::Process->instance->reap_child;
## Free zombie sendmail process.
#Sympa::Process->instance->reap_child;
}
1;
......
......@@ -60,8 +60,8 @@ sub _init {
# Process grouped notifications.
Sympa::Alarm->instance->flush;
} elsif ($state == 2) {
# Free zombie sendmail process.
Sympa::Process->instance->reap_child;
## Free zombie sendmail process.
#Sympa::Process->instance->reap_child;
}
1;
......
......@@ -66,8 +66,8 @@ sub _init {
$self->{_msgid_cleanup} = time;
}
} elsif ($state == 2) {
# Free zombie sendmail process.
Sympa::Process->instance->reap_child;
## Free zombie sendmail process.
#Sympa::Process->instance->reap_child;
}
1;
......
......@@ -68,8 +68,8 @@ sub _init {
? $self->{log_level}
: $Conf::Conf{'log_level'};
# Free zombie sendmail process.
Sympa::Process->instance->reap_child;
## Free zombie sendmail process.
#Sympa::Process->instance->reap_child;
Sympa::List::init_list_cache();
# Process grouped notifications
......@@ -104,7 +104,7 @@ sub _fork_children {
if ($Conf::Conf{'bulk_wait_to_fork'} < time - $self->{_last_check}) {
# Clean up PID file (in case some child bulks would have died)
$process->reap_child(children => $self->{_pids}, file => 1);
$process->sync_child(hash => $self->{_pids}, file => 1);
# Start new processes if there remain at least
# 'bulk_fork_threshold' packets to send in the bulk spool.
......
......@@ -166,8 +166,8 @@ while (not $spindle->{finish}) {
# Purge grouped notifications.
Sympa::Alarm->instance->flush(purge => 1);
# Free zombie sendmail processes.
Sympa::Process->instance->reap_child;
## Free zombie sendmail processes.
#Sympa::Process->instance->reap_child;
$log->syslog('notice', 'Archived exited normally due to signal');
$process->remove_pid(final => 1);
......
......@@ -164,8 +164,8 @@ while (not $spindle->{finish}) {
# Purge grouped notifications
Sympa::Alarm->instance->flush(purge => 1);
# Free zombie sendmail process.
Sympa::Process->instance->reap_child;
## Free zombie sendmail process.
#Sympa::Process->instance->reap_child;
$log->syslog('notice', 'Bulk exited normally due to signal');
$process->remove_pid;
......
......@@ -183,8 +183,8 @@ while (not $spindle->{finish} or $spindle->{finish} ne 'term') {
# Purge grouped notifications
Sympa::Alarm->instance->flush(purge => 1);
# Free zombie sendmail processes.
Sympa::Process->instance->reap_child;
## Free zombie sendmail processes.
#Sympa::Process->instance->reap_child;
$log->syslog('notice', 'Sympa/automatic exited normally due to signal');
$process->remove_pid(final => 1);
......
......@@ -404,7 +404,7 @@ while (!$end) {
sleep 60;
## Free zombie sendmail processes
Sympa::Process->instance->reap_child;
#Sympa::Process->instance->reap_child;
}
# Purge grouped notifications
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment