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   201642 use strict;
  3         9  
  3         90  
4 3     3   15 use warnings;
  3         6  
  3         90  
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   1161 use Linux::Perl;
  3         9  
  3         96  
37 3     3   1257 use Linux::Perl::Constants::Fcntl;
  3         9  
  3         84  
38 3     3   1233 use Linux::Perl::EasyPack;
  3         9  
  3         90  
39 3     3   2271 use Linux::Perl::ParseFlags;
  3         9  
  3         90  
40 3     3   1548 use Linux::Perl::SigSet;
  3         9  
  3         1155  
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 935454 my ($class, %opts) = @_;
63              
64 16         356 local ($!, $^E);
65              
66 16   66     378 my $arch_module = $class->can('NR_epoll_create') && $class;
67 16   66     140 $arch_module ||= do {
68 8         1675 require Linux::Perl::ArchLoader;
69 8         66 Linux::Perl::ArchLoader::get_arch_module($class);
70             };
71              
72 16         185 my $flags = Linux::Perl::ParseFlags::parse( $class, $opts{'flags'} );
73              
74 16         62 my $call_name = 'NR_epoll_create';
75              
76 16         50 my $fd;
77              
78 16 100       90 if ($flags) {
79 4         27 $call_name .= '1';
80              
81 4         107 $fd = Linux::Perl::call( $arch_module->$call_name(), 0 + $flags );
82             }
83             else {
84 12   50     222 $opts{'size'} ||= 1;
85              
86 12         266 $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         45 my $fh;
92              
93 16 100       98 if ( !($flags & _flag_CLOEXEC()) ) {
94 12         426 open $fh, '+<&=' . $fd;
95             }
96              
97             # NB: tests access the filehandle directly.
98 16         269 return bless [$fd, $fh], $arch_module;
99             }
100              
101             sub DESTROY {
102 16     16   19407 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       170 $self->[1] || do {
107 4         68 local $^F = 1 + $self->[0];
108 4         243 open my $temp_fh, '+<&=' . $self->[0];
109             };
110              
111 16         560 return;
112             }
113              
114             my ($epoll_event_keys_ar, $epoll_event_pack);
115              
116             BEGIN {
117 3     3   48 my $arch_is_64bit = (8 == length pack 'L!');
118              
119 3 50       18 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         15 ($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         2439 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   18 };
  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 8070 my ($self, $fd_or_fh, @opts_kv) = @_;
194              
195 8         76 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 55 my ($self, $fd_or_fh, @opts_kv) = @_;
206              
207 2         11 return $self->_add_or_modify( _EPOLL_CTL_MOD(), $fd_or_fh, @opts_kv );
208             }
209              
210             sub _opts_to_event {
211 10     10   29 my ($opts_hr) = @_;
212              
213 10 50 33     76 if (!$opts_hr->{'events'} || !@{ $opts_hr->{'events'} }) {
  10         55  
214 0         0 die 'Need events!';
215             }
216              
217 10         22 my $events = 0;
218 10         29 for my $evtname ( @{ $opts_hr->{'events'} } ) {
  10         43  
219 12   33     83 $events |= EVENT_NUMBER()->{$evtname} || _EVENT_FLAGS()->{$evtname} || do {
220             die "Unknown event '$evtname'";
221             };
222             }
223              
224 10         89 return pack $epoll_event_pack, $events, $opts_hr->{'data'};
225             }
226              
227             sub _add_or_modify {
228 10     10   71 my ($self, $op, $fd_or_fh, %opts) = @_;
229              
230 10 50       68 my $fd = ref($fd_or_fh) ? fileno($fd_or_fh) : $fd_or_fh;
231              
232 10 50       86 if (!defined $opts{'data'}) {
233 10         45 $opts{'data'} = $fd;
234             }
235              
236 10         51 my $event_packed = _opts_to_event(\%opts);
237              
238 10         142 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         51 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 595 my ($self, $fd_or_fh) = @_;
259              
260 2 50       23 my $fd = ref($fd_or_fh) ? fileno($fd_or_fh) : $fd_or_fh;
261              
262 2         89 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         9 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 43779 my ($self, %opts) = @_;
312              
313 16         46 my $sigmask;
314              
315 16         67 my $call_name = 'NR_epoll_';
316 16 100       59 if ($opts{'sigmask'}) {
317 2         14 $call_name .= 'pwait';
318 2         5 $sigmask = Linux::Perl::SigSet::from_list( @{$opts{'sigmask'}} );
  2         16  
319             }
320             else {
321 14         69 $call_name .= 'wait';
322             }
323              
324 16         75 my $blank_event = pack $epoll_event_pack;
325 16         49 my $buf = $blank_event x $opts{'maxevents'};
326              
327 16         53 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     219 0 + $opts{'maxevents'},
334             0 + $timeout,
335             ( (defined($sigmask) && length($sigmask))
336             ? ( $sigmask, length $sigmask )
337             : (),
338             ),
339             );
340              
341 16         90 my @events;
342 16         129 for (1 .. $count) {
343 8         65 my ($events_num, $data) = unpack( $epoll_event_pack, substr( $buf, 0, length($blank_event), q<> ) );
344              
345 8         43 push @events, {
346             events => $events_num,
347             data => $data,
348             };
349             }
350              
351 16         214 return @events;
352             }
353              
354             1;