File Coverage

blib/lib/IO/Async/Loop/Ppoll.pm
Criterion Covered Total %
statement 75 75 100.0
branch 7 10 70.0
condition 7 11 63.6
subroutine 16 16 100.0
pod 4 4 100.0
total 109 116 93.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2007-2020 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::Loop::Ppoll;
7              
8 13     13   5753 use strict;
  13         26  
  13         314  
9 13     13   53 use warnings;
  13         21  
  13         433  
10              
11             our $VERSION = '0.10';
12 13     13   60 use constant API_VERSION => '0.76';
  13         18  
  13         863  
13              
14 13     13   5507 use IO::Async::Loop::Poll 0.76;
  13         207094  
  13         345  
15 13     13   79 use base qw( IO::Async::Loop::Poll );
  13         17  
  13         1061  
16              
17 13     13   68 use Carp;
  13         17  
  13         584  
18              
19 13     13   4976 use IO::Ppoll qw( POLLIN POLLOUT POLLHUP );
  13         13734  
  13         726  
20              
21 13     13   79 use POSIX qw( EINTR SIG_BLOCK SIG_UNBLOCK sigprocmask sigaction );
  13         24  
  13         84  
22              
23 13     13   1145 use constant _CAN_WATCHDOG => 1;
  13         19  
  13         619  
24 13     13   65 use constant WATCHDOG_ENABLE => IO::Async::Loop->WATCHDOG_ENABLE;
  13         19  
  13         7384  
25              
26             =head1 NAME
27              
28             C - use C with C
29              
30             =head1 SYNOPSIS
31              
32             use IO::Async::Loop::Ppoll;
33              
34             my $loop = IO::Async::Loop::Ppoll->new();
35              
36             $loop->add( ... );
37              
38             $loop->add( IO::Async::Signal->new(
39             name =< 'HUP',
40             on_receipt => sub { ... },
41             ) );
42              
43             $loop->run;
44              
45             =head1 DESCRIPTION
46              
47             This subclass of C uses an C object instead
48             of a C to perform read-ready and write-ready tests so that they can
49             be mixed with signal handling.
50              
51             The C system call atomically switches the process's signal mask,
52             performs a wait exactly as C would, then switches it back. This allows
53             a process to block the signals it cares about, but switch in an empty signal
54             mask during the poll, allowing it to handle file IO and signals concurrently.
55              
56             =head1 CONSTRUCTOR
57              
58             =cut
59              
60             =head2 new
61              
62             $loop = IO::Async::Loop::Ppoll->new( %args )
63              
64             This function returns a new instance of a C object.
65             It takes the following named arguments:
66              
67             =over 8
68              
69             =item C
70              
71             The C object to use for notification. Optional; if a value is not
72             given, a new C object will be constructed.
73              
74             =back
75              
76             =cut
77              
78             sub new
79             {
80 12     12 1 127 my $class = shift;
81 12         29 my ( %args ) = @_;
82              
83 12         25 my $poll = delete $args{poll};
84              
85 12   33     66 $poll ||= IO::Ppoll->new();
86              
87 12         304 my $self = $class->SUPER::new( %args, poll => $poll );
88              
89 12         680 $self->{signals} = {};
90              
91 12         29 return $self;
92             }
93              
94             =head1 METHODS
95              
96             As this is a subclass of L, all of its methods are
97             inherited. Expect where noted below, all of the class's methods behave
98             identically to C.
99              
100             =cut
101              
102             sub DESTROY
103             {
104 12     12   46980 my $self = shift;
105              
106 12         92 foreach my $signal ( keys %{ $self->{signals} } ) {
  12         505  
107 6         130 $self->unwatch_signal( $signal );
108             }
109             }
110              
111             =head2 loop_once
112              
113             $count = $loop->loop_once( $timeout )
114              
115             This method calls the C method on the stored C object,
116             passing in the value of C<$timeout>, and processes the results of that call.
117             It returns the total number of C callbacks invoked, or
118             C if the underlying C method returned an error. If the
119             C was interrupted by a signal, then 0 is returned instead.
120              
121             =cut
122              
123             sub loop_once
124             {
125 62     62 1 165421 my $self = shift;
126 62         121 my ( $timeout ) = @_;
127              
128 62         329 $self->_adjust_timeout( \$timeout );
129              
130 62         1765 my $poll = $self->{poll};
131              
132 62         345 $self->pre_wait;
133 62         8271 my $pollret = $poll->poll( $timeout );
134 62         17113700 $self->post_wait;
135              
136 62 50 66     1960 return undef if ( !defined $pollret or $pollret == -1 ) and $! != EINTR;
      66        
137              
138 62         126 my $count = 0;
139              
140 62         79 alarm( IO::Async::Loop->WATCHDOG_INTERVAL ) if WATCHDOG_ENABLE;
141              
142 62         123 my $signals = $self->{signals};
143 62         322 foreach my $sigslot ( values %$signals ) {
144 23 100       138 if( $sigslot->[1] ) {
145 22         98 $sigslot->[0]->();
146 22         1301 $sigslot->[1] = 0;
147 22         48 $count++;
148             }
149             }
150              
151 62         477 return $count + $self->post_poll();
152             }
153              
154             # override
155             sub watch_signal
156             {
157 9     9 1 17596 my $self = shift;
158 9         58 my ( $signal, $code ) = @_;
159              
160 9 100       370 exists $SIG{$signal} or croak "Unrecognised signal name $signal";
161              
162             # We cannot simply set $SIG{$signal} = $code here, because of perl bug
163             # http://rt.perl.org/rt3/Ticket/Display.html?id=82040
164             # Instead, we'll store a tiny piece of code that just sets a flag, and
165             # check the flags on return from the epoll_pwait call.
166              
167 8         202 $self->{signals}{$signal} = [ $code, 0, $SIG{$signal} ];
168 8         79 my $pending = \$self->{signals}{$signal}[1];
169              
170 8         193 my $signum = $self->signame2num( $signal );
171 8         3679 sigprocmask( SIG_BLOCK, POSIX::SigSet->new( $signum ) );
172              
173             # Note this is an unsafe signal handler, and as such it should do as little
174             # as possible.
175 8     22   429 my $sigaction = POSIX::SigAction->new( sub { $$pending = 1 } );
  22         1707460  
176 8 50       564 sigaction( $signum, $sigaction ) or croak "Unable to sigaction - $!";
177             }
178              
179             # override
180             sub unwatch_signal
181             {
182 8     8 1 2598 my $self = shift;
183 8         56 my ( $signal ) = @_;
184              
185 8 50       82 exists $SIG{$signal} or croak "Unrecognised signal name $signal";
186              
187             # When we saved the original value, we might have got an undef. But %SIG
188             # doesn't like having undef assigned back in, so we need to translate
189 8   100     250 $SIG{$signal} = $self->{signals}{$signal}[2] || 'DEFAULT';
190              
191 8         368 delete $self->{signals}{$signal};
192            
193 8         104 my $signum = $self->signame2num( $signal );
194              
195 8         1193 sigprocmask( SIG_UNBLOCK, POSIX::SigSet->new( $signum ) );
196             }
197              
198             =head1 SEE ALSO
199              
200             =over 4
201              
202             =item *
203              
204             L - Object interface to Linux's C call
205              
206             =item *
207              
208             L - a set using an C object
209              
210             =back
211              
212             =head1 AUTHOR
213              
214             Paul Evans
215              
216             =cut
217              
218             0x55AA;