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   194115 use strict;
  3         15  
  3         102  
4 3     3   21 use warnings;
  3         15  
  3         99  
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   1128 use Linux::Perl;
  3         9  
  3         84  
37 3     3   1128 use Linux::Perl::Constants::Fcntl;
  3         6  
  3         90  
38 3     3   1314 use Linux::Perl::EasyPack;
  3         12  
  3         90  
39 3     3   1176 use Linux::Perl::ParseFlags;
  3         6  
  3         90  
40 3     3   1263 use Linux::Perl::SigSet;
  3         6  
  3         1179  
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 920883 my ($class, %opts) = @_;
63              
64 16         327 local ($!, $^E);
65              
66 16   66     305 my $arch_module = $class->can('NR_epoll_create') && $class;
67 16   66     117 $arch_module ||= do {
68 8         1547 require Linux::Perl::ArchLoader;
69 8         54 Linux::Perl::ArchLoader::get_arch_module($class);
70             };
71              
72 16         166 my $flags = Linux::Perl::ParseFlags::parse( $class, $opts{'flags'} );
73              
74 16         73 my $call_name = 'NR_epoll_create';
75              
76 16         48 my $fd;
77              
78 16 100       88 if ($flags) {
79 4         24 $call_name .= '1';
80              
81 4         119 $fd = Linux::Perl::call( $arch_module->$call_name(), 0 + $flags );
82             }
83             else {
84 12   50     158 $opts{'size'} ||= 1;
85              
86 12         242 $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         50 my $fh;
92              
93 16 100       105 if ( !($flags & _flag_CLOEXEC()) ) {
94 12         392 open $fh, '+<&=' . $fd;
95             }
96              
97             # NB: tests access the filehandle directly.
98 16         229 return bless [$fd, $fh], $arch_module;
99             }
100              
101             sub DESTROY {
102 16     16   18279 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       151 $self->[1] || do {
107 4         53 local $^F = 1 + $self->[0];
108 4         210 open my $temp_fh, '+<&=' . $self->[0];
109             };
110              
111 16         400 return;
112             }
113              
114             my ($epoll_event_keys_ar, $epoll_event_pack);
115              
116             BEGIN {
117 3     3   36 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         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         2259 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 9812 my ($self, $fd_or_fh, @opts_kv) = @_;
194              
195 8         94 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 89 my ($self, $fd_or_fh, @opts_kv) = @_;
206              
207 2         13 return $self->_add_or_modify( _EPOLL_CTL_MOD(), $fd_or_fh, @opts_kv );
208             }
209              
210             sub _opts_to_event {
211 10     10   25 my ($opts_hr) = @_;
212              
213 10 50 33     64 if (!$opts_hr->{'events'} || !@{ $opts_hr->{'events'} }) {
  10         54  
214 0         0 die 'Need events!';
215             }
216              
217 10         25 my $events = 0;
218 10         34 for my $evtname ( @{ $opts_hr->{'events'} } ) {
  10         46  
219 12   33     114 $events |= EVENT_NUMBER()->{$evtname} || _EVENT_FLAGS()->{$evtname} || do {
220             die "Unknown event '$evtname'";
221             };
222             }
223              
224 10         91 return pack $epoll_event_pack, $events, $opts_hr->{'data'};
225             }
226              
227             sub _add_or_modify {
228 10     10   61 my ($self, $op, $fd_or_fh, %opts) = @_;
229              
230 10 50       72 my $fd = ref($fd_or_fh) ? fileno($fd_or_fh) : $fd_or_fh;
231              
232 10 50       73 if (!defined $opts{'data'}) {
233 10         58 $opts{'data'} = $fd;
234             }
235              
236 10         48 my $event_packed = _opts_to_event(\%opts);
237              
238 10         124 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         52 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 642 my ($self, $fd_or_fh) = @_;
259              
260 2 50       29 my $fd = ref($fd_or_fh) ? fileno($fd_or_fh) : $fd_or_fh;
261              
262 2         91 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 41621 my ($self, %opts) = @_;
312              
313 16         48 my $sigmask;
314              
315 16         48 my $call_name = 'NR_epoll_';
316 16 100       79 if ($opts{'sigmask'}) {
317 2         7 $call_name .= 'pwait';
318 2         5 $sigmask = Linux::Perl::SigSet::from_list( @{$opts{'sigmask'}} );
  2         17  
319             }
320             else {
321 14         41 $call_name .= 'wait';
322             }
323              
324 16         67 my $blank_event = pack $epoll_event_pack;
325 16         55 my $buf = $blank_event x $opts{'maxevents'};
326              
327 16         64 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     206 0 + $opts{'maxevents'},
334             0 + $timeout,
335             ( (defined($sigmask) && length($sigmask))
336             ? ( $sigmask, length $sigmask )
337             : (),
338             ),
339             );
340              
341 16         107 my @events;
342 16         106 for (1 .. $count) {
343 8         67 my ($events_num, $data) = unpack( $epoll_event_pack, substr( $buf, 0, length($blank_event), q<> ) );
344              
345 8         51 push @events, {
346             events => $events_num,
347             data => $data,
348             };
349             }
350              
351 16         240 return @events;
352             }
353              
354             1;