Skip to content

Commit f580c3b

Browse files
authored
Merge pull request #33 from cfconrad/wip_emit_in_signal_handler
Introduce emit_from_sigchld()
2 parents e19d4b3 + b442062 commit f580c3b

6 files changed

Lines changed: 120 additions & 22 deletions

File tree

cpanfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on configure => sub {
88

99
on test => sub {
1010
requires 'Test::More';
11+
requires 'Test::Exception';
1112
};
1213
on develop => sub {
1314
requires 'Devel::Cover::Report::Codecovbash';

lib/Mojo/IOLoop/ReadWriteProcess.pm

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ our $VERSION = '0.31';
44

55
use Mojo::Base 'Mojo::EventEmitter';
66
use Mojo::File 'path';
7-
use Mojo::Util qw(b64_decode b64_encode);
7+
use Mojo::Util qw(b64_decode b64_encode scope_guard);
88
use Mojo::IOLoop::Stream;
99

1010
use Mojo::IOLoop::ReadWriteProcess::Exception;
@@ -477,18 +477,25 @@ sub start {
477477
$self->_status(undef);
478478
$self->session->enable;
479479

480+
{
481+
my $old_emit_from_sigchld = $self->session->emit_from_sigchld;
482+
$self->session->emit_from_sigchld(0);
483+
my $scope_guard = scope_guard sub {
484+
$self->session->emit_from_sigchld($old_emit_from_sigchld);
485+
$self->session->consume_collected_info if ($old_emit_from_sigchld);
486+
};
487+
488+
if ($self->code) {
489+
$self->_fork($self->code, @args);
490+
}
491+
elsif ($self->execute) {
492+
$self->_open($self->execute, @args);
493+
}
480494

481-
if ($self->code) {
482-
$self->_fork($self->code, @args);
495+
$self->write_pidfile;
496+
$self->emit('start');
497+
$self->session->register($self->pid() => $self);
483498
}
484-
elsif ($self->execute) {
485-
$self->_open($self->execute, @args);
486-
}
487-
488-
$self->write_pidfile;
489-
$self->emit('start');
490-
$self->session->register($self->pid() => $self);
491-
492499
return $self;
493500
}
494501

lib/Mojo/IOLoop/ReadWriteProcess/Session.pm

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ has orphans => sub { {} };
2323
has process_table => sub { {} };
2424
has collected_info => sub { [] };
2525
has 'handler';
26+
has emit_from_sigchld => 1;
2627

2728
my $singleton;
2829

@@ -55,6 +56,7 @@ sub enable {
5556
while ((my $pid = waitpid(-1, WNOHANG)) > 0) {
5657
$singleton->add_collected_info($pid, $?, $!);
5758
}
59+
$singleton->consume_collected_info() if ($singleton->emit_from_sigchld());
5860
}
5961
});
6062
}
@@ -133,7 +135,7 @@ sub contains {
133135
$singleton->all->grep(sub { $_->pid eq $pid })->size == 1;
134136
}
135137

136-
sub reset { @{+shift}{qw(events orphans process_table collected_info handler)} = ({}, {}, {}, [], undef) }
138+
sub reset { @{+shift}{qw(events orphans process_table collected_info handler emit_from_sigchld)} = ({}, {}, {}, [], undef, 1) }
137139

138140
# XXX: This should be replaced by PR_GET_CHILD_SUBREAPER
139141
sub disable_subreaper {

t/01_run.t

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
use warnings;
44
use strict;
55
use Test::More;
6+
use Test::Exception;
67
use POSIX;
78
use FindBin;
9+
use IO::Select;
810
use Mojo::File qw(tempfile path);
911
use lib ("$FindBin::Bin/lib", "../lib", "lib");
12+
use Mojo::IOLoop::ReadWriteProcess qw(process);
13+
use Mojo::IOLoop::ReadWriteProcess::Test::Utils qw(attempt);
1014

1115
subtest process => sub {
12-
use Mojo::IOLoop::ReadWriteProcess;
1316

1417
my $c = Mojo::IOLoop::ReadWriteProcess->new();
1518

@@ -26,7 +29,6 @@ subtest process => sub {
2629
};
2730

2831
subtest 'process basic functions' => sub {
29-
use Mojo::IOLoop::ReadWriteProcess;
3032

3133
my $p = Mojo::IOLoop::ReadWriteProcess->new();
3234
eval {
@@ -66,7 +68,6 @@ subtest 'process basic functions' => sub {
6668
};
6769

6870
subtest 'process is_running()' => sub {
69-
use Mojo::IOLoop::ReadWriteProcess;
7071

7172
my @output;
7273
pipe(PARENT, CHILD);
@@ -139,7 +140,6 @@ subtest 'process execute()' => sub {
139140
plan skip_all =>
140141
"You do not seem to have $test_script_sigtrap. The script is required to run the test"
141142
unless -e $test_script_sigtrap;
142-
use Mojo::IOLoop::ReadWriteProcess;
143143
my $p = Mojo::IOLoop::ReadWriteProcess->new(
144144
sleeptime_during_kill => 0.1,
145145
execute => $test_script
@@ -302,15 +302,12 @@ subtest 'process execute()' => sub {
302302
};
303303

304304
subtest 'process(execute =>"/usr/bin/true")' => sub {
305-
use Mojo::IOLoop::ReadWriteProcess qw(process);
306305
plan skip_all => "Missing '/usr/bin/true'" unless -e '/usr/bin/true';
307306

308307
is(process(execute => '/usr/bin/true')->quirkiness(1)->start()->wait_stop()->exit_status(), 0, 'Simple exec of "/usr/bin/true" return 0');
309308
};
310309

311310
subtest 'process code()' => sub {
312-
use Mojo::IOLoop::ReadWriteProcess;
313-
use IO::Select;
314311
my $p = Mojo::IOLoop::ReadWriteProcess->new(
315312
kill_sleeptime => 0.01,
316313
sleeptime_during_kill => 0.01,
@@ -548,7 +545,6 @@ process';
548545
};
549546

550547
subtest 'process_args' => sub {
551-
use Mojo::IOLoop::ReadWriteProcess;
552548
my $code = sub {
553549
shift;
554550
print $_.$/ for(@_);
@@ -578,4 +574,18 @@ subtest 'process in process' => sub {
578574
is ($p->read_all_stdout(), 'DONE', "Use ReadWriteProcess inside of ReadWriteProcess(code=>'')");
579575
};
580576

577+
subtest 'execute exeption handling' => sub {
578+
throws_ok {
579+
process(execute => '/I/do/not/exist')->start()->wait_stop()->exit_status();
580+
} qr%/I/do/not/exist%, 'Execute throw exception, if executable does not exists';
581+
582+
my $p = process(execute => 'sleep 0.2')->start();
583+
attempt {
584+
attempts => 20,
585+
condition => sub { defined($p->exit_status)},
586+
};
587+
is ($p->is_running(), 0, 'Process not running');
588+
is ($p->exit_status(), 0, 'Exit status is 0');
589+
};
590+
581591
done_testing;

t/06_events.t

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,82 @@ subtest collect_status => sub {
9797
is(Mojo::IOLoop::ReadWriteProcess::Session->singleton->all->size, 1);
9898
};
9999

100+
subtest collect_from_signal_handler => sub {
101+
my $p = process(execute => '/usr/bin/true');
102+
my $collected = 0;
103+
my $orphan = 0;
104+
my $sig_chld = 0;
105+
$p->session->reset();
106+
$p->session->collect_status(1); # needed, because previous test set it to 0
107+
$p->session->on(SIG_CHLD => sub { $sig_chld++});
108+
$p->session->on(collected => sub { $collected++ });
109+
$p->session->on(collected_orphan => sub { $orphan++ });
110+
$p->start();
111+
112+
attempt {
113+
attempts => 10,
114+
condition => sub { $sig_chld > 0 && $collected > 0},
115+
};
116+
117+
is($sig_chld, 1, "Event for SIG_CHILD was emitted");
118+
is($collected, 1, "Event collected apear without doing active wait()");
119+
is($orphan, 0, "No orphans where collected");
120+
121+
$p->wait_stop();
122+
is($collected, 1, "No more collect events emitted");
123+
is($orphan, 0, "No more orphans events emitted");
124+
is($p->exit_status, 0 , '/usr/bin/true exited with 0');
125+
126+
exec ('/usr/bin/true') if (fork() == 0);
127+
128+
attempt {
129+
attempts => 10,
130+
condition => sub { $sig_chld > 1 && $orphan > 0},
131+
};
132+
133+
is($sig_chld, 2, "Event for SIG_CHILD was emitted");
134+
is($collected, 1, "No more collect events emitted (2)");
135+
is($orphan, 1, "Collect one orphan");
136+
};
137+
138+
subtest emit_from_sigchld_off => sub {
139+
my $p = process(execute => '/usr/bin/true');
140+
my $collected = 0;
141+
my $orphan = 0;
142+
my $sig_chld = 0;
143+
$p->session->reset();
144+
$p->session->collect_status(1);
145+
$p->session->emit_from_sigchld(0);
146+
$p->session->on(SIG_CHLD => sub { $sig_chld++});
147+
$p->session->on(collected => sub { $collected++ });
148+
$p->session->on(collected_orphan => sub { $orphan++ });
149+
$p->start();
150+
151+
attempt {
152+
attempts => 10,
153+
condition => sub { $sig_chld > 0},
154+
};
155+
is($sig_chld, 1, "Event for SIG_CHILD was emitted");
156+
is($collected, 0, "Event collected didn't appear from sighandler");
157+
is($orphan, 0, "No orphans where collected");
158+
159+
$p->wait_stop();
160+
is($collected, 1, "No more collect events emitted");
161+
is($orphan, 0, "No more orphans events emitted");
162+
is($p->exit_status, 0 , '/usr/bin/true exited with 0');
163+
164+
exec ('/usr/bin/true') if (fork() == 0);
165+
attempt {
166+
attempts => 10,
167+
condition => sub { $sig_chld > 1},
168+
};
169+
is($collected, 1, "No more collect events emitted (2)");
170+
is($orphan, 0, "collect_orphan didn't appear from sighandler");
171+
172+
$p->session->consume_collected_info();
173+
is($sig_chld, 2, "Event for SIG_CHILD was emitted");
174+
is($collected, 1, "No more collect events emitted (3)");
175+
is($orphan, 1, "Collect one orphan");
176+
};
100177

101178
done_testing();

t/lib/Mojo/IOLoop/ReadWriteProcess/Test/Utils.pm

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
package Mojo::IOLoop::ReadWriteProcess::Test::Utils;
22

33
our @EXPORT_OK = qw(attempt);
4-
4+
use Time::HiRes qw(sleep);
55
use Exporter 'import';
66
use constant DEBUG => $ENV{MOJO_PROCESS_DEBUG};
77

88
sub attempt {
99
my $attempts = 0;
1010
my ($total_attempts, $condition, $cb, $or)
1111
= ref $_[0] eq 'HASH' ? (@{$_[0]}{qw(attempts condition cb or)}) : @_;
12+
$cb //= sub {1};
1213
until ($condition->() || $attempts >= $total_attempts) {
1314
warn "Attempt $attempts" if DEBUG;
1415
$cb->();
15-
sleep 1;
16+
sleep .1;
1617
$attempts++;
1718
}
1819
$or->() if $or && !$condition->();

0 commit comments

Comments
 (0)