File Coverage

blib/lib/Linux/Perl/epoll.pm
Criterion Covered Total %
statement 90 91 98.9
branch 15 20 75.0
condition 9 17 52.9
subroutine 17 17 100.0
pod 5 5 100.0
total 136 150 90.6


line stmt bran cond sub pod time code
1             package Linux::Perl::epoll;
2              
3 3     3   175335 use strict;
  3         9  
  3         69  
4 3     3   15 use warnings;
  3         9  
  3         93  
5              
6             =encoding utf-8
7              
8             =head1 NAME
9              
10             Linux::Perl::epoll
11              
12             =head1 SYNOPSIS
13              
14             my $epl = Linux::Perl::epoll->new();
15              
16             $epl->add( $fh, events => ['IN', 'ET'] );
17              
18             my @events = $epl->wait(
19             maxevents => 3,
20             timeout => 2, #seconds
21             sigmask => ['INT', 'TERM'], #optional
22             );
23              
24             $epl->delete($fh);
25              
26             =head1 DESCRIPTION
27              
28             An interface to Linux’s “epoll” feature.
29              
30             Note that older kernel versions may not support all of the functionality
31             documented here. Check your system’s epoll documentation (i.e.,
32             C and the various system calls’ pages) for full details.
33              
34             =cut
35              
36 3     3   1242 use Linux::Perl;
  3         6  
  3         96  
37 3     3   918 use Linux::Perl::Constants::Fcntl;
  3         6  
  3         69  
38 3     3   978 use Linux::Perl::EasyPack;
  3         6  
  3         72  
39 3     3   1371 use Linux::Perl::ParseFlags;
  3         6  
  3         75  
40 3     3   972 use Linux::Perl::SigSet;
  3         6  
  3         909  
41              
42             *_flag_CLOEXEC = \*Linux::Perl::Constants::Fcntl::flag_CLOEXEC;
43              
44             =head1 METHODS
45              
46             =head2 I->new( %OPTS )
47              
48             Creates a new epoll instance. %OPTS are:
49              
50             =over
51              
52             =item * C - Currently only C is recognized.
53              
54             =item * C - Optional, and only useful on pre-2.6.8 kernels.
55             See C
for more details.
56              
57             =back
58              
59             =cut
60              
61             sub new {
62 16     16 1 1113826 my ($class, %opts) = @_;
63              
64 16         224 local ($!, $^E);
65              
66 16   66     212 my $arch_module = $class->can('NR_epoll_create') && $class;
67 16   66     95 $arch_module ||= do {
68 8         1262 require Linux::Perl::ArchLoader;
69 8         44 Linux::Perl::ArchLoader::get_arch_module($class);
70             };
71              
72 16         133 my $flags = Linux::Perl::ParseFlags::parse( $class, $opts{'flags'} );
73              
74 16         53 my $call_name = 'NR_epoll_create';
75              
76 16         28 my $fd;
77              
78 16 100       58 if ($flags) {
79 4         19 $call_name .= '1';
80              
81 4         85 $fd = Linux::Perl::call( $arch_module->$call_name(), 0 + $flags );
82             }
83             else {
84 12   50     154 $opts{'size'} ||= 1;
85              
86 12         190 $fd = Linux::Perl::call( $arch_module->$call_name(), 0 + $opts{'size'} );
87             }
88              
89             # Force the CLOEXEC behavior that Perl imposes on its file handles
90             # unless the CLOEXEC flag was given explicitly.
91 16         34 my $fh;
92              
93 16 100       81 if ( !($flags & _flag_CLOEXEC()) ) {
94 12         328 open $fh, '+<&=' . $fd;
95             }
96              
97             # NB: tests access the filehandle directly.
98 16         189 return bless [$fd, $fh], $arch_module;
99             }
100              
101             sub DESTROY {
102 16     16   44584 my ($self) = @_;
103              
104             # Create a Perl filehandle for the file descriptor so
105             # that we get a close() when the filehandle object goes away.
106 16 100       107 $self->[1] || do {
107 4         37 local $^F = 1 + $self->[0];
108 4         144 open my $temp_fh, '+<&=' . $self->[0];
109             };
110              
111 16         321 return;
112             }
113              
114             my ($epoll_event_keys_ar, $epoll_event_pack);
115              
116             BEGIN {
117 3     3   27 my $arch_is_64bit = (8 == length pack 'L!');
118              
119 3 50       15 my @_epoll_event_src = (
120             events => 'L', #uint32_t
121             (
122             $arch_is_64bit
123             ? ( data => 'Q' )
124             : (
125             q<> => 'xxxx',
126             data => 'L!', #uint64_t
127             ),
128             ),
129             );
130              
131 3         12 ($epoll_event_keys_ar, $epoll_event_pack) = Linux::Perl::EasyPack::split_pack_list(@_epoll_event_src);
132             }
133              
134             #----------------------------------------------------------------------
135              
136             =head2 I->EVENT_NUMBER()
137              
138             Returns a (constant) hash reference that cross-references event names
139             and their numbers. This is useful, e.g., for parsing events from the return
140             of C.
141              
142             The recognized event names are C, C, C, C, C,
143             and C.
144              
145             =cut
146              
147             use constant {
148 3         1830 EVENT_NUMBER => {
149             IN => 1,
150             OUT => 4,
151             RDHUP => 0x2000,
152             PRI => 2,
153             ERR => 8,
154             HUP => 16,
155             },
156              
157             _EPOLL_CTL_ADD => 1,
158             _EPOLL_CTL_DEL => 2,
159             _EPOLL_CTL_MOD => 3,
160              
161             _EVENT_FLAGS => {
162             ET => (1 << 31),
163             ONESHOT => (1 << 30),
164             WAKEUP => (1 << 29),
165             EXCLUSIVE => (1 << 28),
166             },
167 3     3   15 };
  3         6  
168              
169             #----------------------------------------------------------------------
170              
171             =head2 I->add( $FD_OR_FH, %OPTS )
172              
173             Adds a listener to the epoll instance. $FD_OR_FH is either a
174             Perl filehandle or a file descriptor number. %OPTS are:
175              
176             =over
177              
178             =item * C - An array reference of events/switches. Each member
179             is either a key from C or one of the following
180             switches: C, C, C, C. Your kernel
181             may not support all of those; check C for details.
182              
183             =item * C - Optional, an arbitrary number to store with the file
184             descriptor. This defaults to the file descriptor because this is the obvious
185             way to correlate an event with its filehandle; however, you can set your own
186             numeric value here if you’d rather.
187              
188             =back
189              
190             =cut
191              
192             sub add {
193 8     8 1 7972 my ($self, $fd_or_fh, @opts_kv) = @_;
194              
195 8         66 return $self->_add_or_modify( _EPOLL_CTL_ADD(), $fd_or_fh, @opts_kv );
196             }
197              
198             =head2 I->modify( $FD_OR_FH, %OPTS )
199              
200             Same arguments as C; use this to update an existing epoll listener.
201              
202             =cut
203              
204             sub modify {
205 2     2 1 38 my ($self, $fd_or_fh, @opts_kv) = @_;
206              
207 2         9 return $self->_add_or_modify( _EPOLL_CTL_MOD(), $fd_or_fh, @opts_kv );
208             }
209              
210             sub _opts_to_event {
211 10     10   22 my ($opts_hr) = @_;
212              
213 10 50 33     50 if (!$opts_hr->{'events'} || !@{ $opts_hr->{'events'} }) {
  10         44  
214 0         0 die 'Need events!';
215             }
216              
217 10         20 my $events = 0;
218 10         21 for my $evtname ( @{ $opts_hr->{'events'} } ) {
  10         33  
219 12   33     63 $events |= EVENT_NUMBER()->{$evtname} || _EVENT_FLAGS()->{$evtname} || do {
220             die "Unknown event '$evtname'";
221             };
222             }
223              
224 10         59 return pack $epoll_event_pack, $events, $opts_hr->{'data'};
225             }
226              
227             sub _add_or_modify {
228 10     10   45 my ($self, $op, $fd_or_fh, %opts) = @_;
229              
230 10 50       50 my $fd = ref($fd_or_fh) ? fileno($fd_or_fh) : $fd_or_fh;
231              
232 10 50       41 if (!defined $opts{'data'}) {
233 10         34 $opts{'data'} = $fd;
234             }
235              
236 10         31 my $event_packed = _opts_to_event(\%opts);
237              
238 10         94 Linux::Perl::call(
239             $self->NR_epoll_ctl(),
240             0 + $self->[0],
241             0 + $op,
242             0 + $fd,
243             $event_packed,
244             );
245              
246 10         39 return $self;
247             }
248              
249             #----------------------------------------------------------------------
250              
251             =head2 I->delete( $FD_OR_FH )
252              
253             Removes an epoll listener.
254              
255             =cut
256              
257             sub delete {
258 2     2 1 540 my ($self, $fd_or_fh) = @_;
259              
260 2 50       13 my $fd = ref($fd_or_fh) ? fileno($fd_or_fh) : $fd_or_fh;
261              
262 2         80 Linux::Perl::call(
263             $self->NR_epoll_ctl(),
264             0 + $self->[0],
265             0 + _EPOLL_CTL_DEL(),
266             0 + $fd,
267             (pack $epoll_event_pack), #accommodate pre-2.6.9 kernels
268             );
269              
270 2         6 return $self;
271             }
272              
273             #----------------------------------------------------------------------
274              
275             =head2 @events = I->wait( %OPTS )
276              
277             Waits for one or more events on the epoll. %OPTS are:
278              
279             =over
280              
281             =item * C - The number of events to listen for.
282              
283             =item * C - in seconds
284              
285             =item * C - Optional, an array of signals to block. The signals
286             can be specified either as names (e.g., C) or as numbers.
287             See C for why you might want to do this. (Note that Perl
288             doesn’t really expect you to block signals directly, so this may screw
289             things up for you in weird ways. If in doubt, avoid this option.)
290              
291             =back
292              
293             The return is a list of hash references, one for each received event.
294             Each hash reference is:
295              
296             =over
297              
298             =item * C - The same number given in C—or, if you didn’t
299             set a custom C value, the file descriptor associated with the event.
300              
301             =item * C - Corresponds to the same-named array given in C,
302             but to optimize performance this is returned as a single number. Check
303             for specific events by iterating through the C hash
304             reference.
305              
306             =back
307              
308             =cut
309              
310             sub wait {
311 16     16 1 59928 my ($self, %opts) = @_;
312              
313 16         29 my $sigmask;
314              
315 16         40 my $call_name = 'NR_epoll_';
316 16 100       56 if ($opts{'sigmask'}) {
317 2         7 $call_name .= 'pwait';
318 2         5 $sigmask = Linux::Perl::SigSet::from_list( @{$opts{'sigmask'}} );
  2         14  
319             }
320             else {
321 14         30 $call_name .= 'wait';
322             }
323              
324 16         65 my $blank_event = pack $epoll_event_pack;
325 16         39 my $buf = $blank_event x $opts{'maxevents'};
326              
327 16         42 my $timeout = int(1000 * $opts{'timeout'});
328              
329             my $count = Linux::Perl::call(
330             $self->$call_name(),
331             0 + $self->[0],
332             $buf,
333 16 100 66     156 0 + $opts{'maxevents'},
334             0 + $timeout,
335             ( (defined($sigmask) && length($sigmask))
336             ? ( $sigmask, length $sigmask )
337             : (),
338             ),
339             );
340              
341 16         46 my @events;
342 16         57 for (1 .. $count) {
343 8         50 my ($events_num, $data) = unpack( $epoll_event_pack, substr( $buf, 0, length($blank_event), q<> ) );
344              
345 8         178 push @events, {
346             events => $events_num,
347             data => $data,
348             };
349             }
350              
351 16         117 return @events;
352             }
353              
354             1;